Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Test/ArrayPass/array_bounds_int64_dynamic.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Red test for a bounds-check bypass on dynamic arrays accessed through
// a non-variable base expression.
//
// TDynamicArrayExpr.EvalAsInteger / EvalAsString / ... (Source/dwsArrayExprs.pas
// around lines 1367..1459) declares the index as a 32-bit `Integer`,
// even though IndexExpr.EvalAsInteger returns an Int64. Any index whose
// low 32 bits land inside the array's length silently bypasses
// BoundsCheckPassed and indexes the wrong element. The function-call base
// here forces the non-`Var` code path so the bug manifests on every platform
// (the variable-base path adds a separate Cardinal-truncation issue that we
// keep out of this test to avoid platform-dependent access violations).
//
// Expected (safe) behaviour: an out-of-bounds exception is raised.
function MakeArr : array of Integer;
begin
Result := new Integer[5];
for var i := 0 to 4 do
Result[i] := 100 + 10*i;
end;

var base : Integer := 0;
base := base + 4294967296; // 2^32; low 32 bits are 0

try
PrintLn(MakeArr()[base]); // truncates to MakeArr()[0] today
PrintLn('FAIL no exception (MakeArr()[2^32])');
except
on E: Exception do PrintLn('OK out-of-bounds raised for 2^32');
end;

try
PrintLn(MakeArr()[base + 4]); // truncates to MakeArr()[4] today
PrintLn('FAIL no exception (MakeArr()[2^32 + 4])');
except
on E: Exception do PrintLn('OK out-of-bounds raised for 2^32 + 4');
end;
2 changes: 2 additions & 0 deletions Test/ArrayPass/array_bounds_int64_dynamic.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
OK out-of-bounds raised for 2^32
OK out-of-bounds raised for 2^32 + 4
45 changes: 45 additions & 0 deletions Test/ArrayPass/array_bounds_int64_static.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Red test for a bounds-check bypass on static arrays.
//
// TStaticArrayExpr.GetIndex (Source/dwsArrayExprs.pas:1250) evaluates the
// index as Int64 but stores `EvalAsInteger(exec) - FLowBound` into a 32-bit
// `Result : Integer` before performing a Cardinal-based range check. Any
// index whose low 32 bits land inside the declared range silently bypasses
// the check and reads (or writes) the wrong element instead of raising
// `Upper bound exceeded`.
//
// Expected (safe) behaviour: an out-of-bounds exception is raised for every
// index >= Length even when bits above 2^31 are set.
var a : array[0..4] of Integer;
var i : Integer;
for i := 0 to 4 do
a[i] := 100 + i;

// Build the indices at runtime so the compiler cannot constant-fold them
// into a static range error.
var base : Integer := 0;
base := base + 4294967296; // 2^32; low 32 bits are 0

try
PrintLn(a[base]); // truncates to a[0] today
PrintLn('FAIL no exception (a[2^32])');
except
on E: Exception do PrintLn('OK out-of-bounds raised for 2^32');
end;

try
PrintLn(a[base + 3]); // truncates to a[3] today
PrintLn('FAIL no exception (a[2^32 + 3])');
except
on E: Exception do PrintLn('OK out-of-bounds raised for 2^32 + 3');
end;

try
a[base + 2] := 999; // truncates write to a[2] today
PrintLn('FAIL no exception on write (a[2^32 + 2])');
except
on E: Exception do PrintLn('OK out-of-bounds raised on write');
end;

// Witness corruption: if the bounds check was bypassed the previous write
// will have clobbered a[2].
PrintLn(a[2]);
4 changes: 4 additions & 0 deletions Test/ArrayPass/array_bounds_int64_static.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
OK out-of-bounds raised for 2^32
OK out-of-bounds raised for 2^32 + 3
OK out-of-bounds raised on write
102