← board

string[N] := <longer> does not truncate — it overruns the buffer

What happens

A string[N] (shortstring) has a fixed capacity of N characters. Assigning a longer value truncates to N — that is the defined behaviour, and it is why the type is safe to place inline in a record or on the stack. pxx does not truncate. It writes the whole source string into the N-byte slot.

program sn2;
{$mode objfpc}
var
  a: string[4];
  b: string[4];
begin
  b := 'BBBB';
  a := 'aaaaaaaaaaaaaaaa';   { 16 chars into a string[4] }
  writeln('a=[', a, '] len=', Length(a));
  writeln('b=[', b, '] len=', Length(b));
  if b <> 'BBBB' then writeln('*** b was CLOBBERED by the assignment to a');
end.

FPC 3.2.2 (correct):

a=[aaaa] len=4
b=[BBBB] len=4

pxx (HEAD, 49728c23):

a=[aaaaaaaaaaaaaaaa] len=16
b=[] len=7016996765293437281
*** b was CLOBBERED by the assignment to a

b's length byte and contents are destroyed: the twelve characters that did not fit in a were written straight over the variable next to it. Length(b) then reads a garbage qword.

Concatenation has the same hole — sh := sh + 'zz' on a string[8] yields a length of 12 under pxx and 8 under FPC.

Why this is worse than a wrong number

Every other string[N] defect we have shipped was loud or local. This one writes outside the object. A string[N] field in a record overruns into the next field; a local overruns into the next local (which is what the repro shows); on the stack it can reach a saved register or a return address. The program keeps running and produces plausible output, so nothing in the test suite has to fail for this to be corrupting data — which is exactly the class the fuzzer exists to catch, and the reason it is filed at prio 78 rather than as a compat- parity item.

Where to look

The truncating store belongs wherever a shortstring assignment is lowered: the source length must be clamped to the declared capacity N (and the stored length byte set to min(len, N)) before the copy, for assignment, concatenation, and passing by value alike. Check Length() reads the clamped byte afterwards.

Acceptance

Log