← board

SetLength on a ShortString does not clamp at its capacity

var s: ShortString; t: string[10]; i: Integer;
SetLength(s, 1000);   { pxx: Length 232, fpc: 255 }
SetLength(t, 50);     { pxx: 50, fpc: 50 too; filling t overran it in both }
for i := 1 to Length(t) do t[i] := 'x';   { pxx: SIGSEGV }

No target or profile builds with frozen strings by default: PXX_MANAGED_STRING is defined unconditionally (paslexer.inc) and -u is opt-in. But a declared ShortString or string[N] is frozen in every build, so the bug reached default programs on all six targets.

Fix

The other growers were measured and already stayed inside the buffer: Insert, concatenation, IntToHex with a wide width, and Str with a width, into a ShortString and into a string[10].

Divergence from fpc

fpc 3.2.2 clamps only at 255. SetLength on a string[10] to 50 reads back 50, and a fill overruns the variable. pxx clamps at N, recorded in devdocs/dev/pascal-dialect-divergences.md. A negative count gives 0 in pxx; fpc keeps its low byte (-1 gives 255, -5 gives 251).

Tests

The first fix skipped itself (5ebf185c24), caught by frankd-90

The first version clamped a variable count through a builtin helper, PXXShortLenClamp, and skipped the clamp when FindProc could not see it. builtin is loaded into a program with no uses clause only when a pre-scan trigger name appears. So frankd-90's var t: string[10]; n := 50; SetLength(t, n) plus a fill still segfaulted on the tip, a negative variable count still gave 251, and p^ of a ShortString still crashed. Adding a HexStr call anywhere made it pass, and both fixtures above called HexStr (or IntToHex through SysUtils), so they were green for the wrong reason. The clamp is now inline and needs nothing loaded.

The same silent-skip shape exists at 37 other FindProc sites: task-a-a-missing-runtime-helper-silently-skips-its-action-in-38-places.