Minimal repro
using System;
using System.Runtime.CompilerServices;
public class Program
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static int Test(int[][] rows, int k, int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
{
sum += rows[k][i];
if (i == 0) rows[k] = new int[1] { 999 }; // rows[k][1] must now throw
}
return sum;
}
public static int Main()
{
int[][] rows = new int[1][];
rows[0] = new int[16];
for (int j = 0; j < 16; j++) rows[0][j] = j + 1;
try { Console.WriteLine($"Actual: sum={Test(rows, 0, 8)}"); return 1; }
catch (IndexOutOfRangeException) { Console.WriteLine("Actual: IndexOutOfRangeException"); return 100; }
}
}
Run: corerun repro.dll (Checked or Release JIT, .NET 11 main @ b44cd90)
Expected
Actual: IndexOutOfRangeException, exit code 100.
Actual
Actual: sum=1, exit code 1 — no exception; the loop reads 7 words past the 1-element row.
Notes
DOTNET_JitCloneLoops=0 restores the exception. optCanOptimizeByLoopCloning checks only the rows/k locals for invariance, never the heap cell rows[k], so the preheader rows[k].Length guard is stale.
The store variant (rows[k][i] = v with new int[1], n = 32) writes 31 words past the row and corrupts the GC heap: Assert failure: m_alignpad == 0 in ObjHeader::Validate (src/coreclr/vm/syncblk.cpp:1840).
Minimal repro
Run:
corerun repro.dll(Checked or Release JIT, .NET 11 main @ b44cd90)Expected
Actual: IndexOutOfRangeException, exit code 100.Actual
Actual: sum=1, exit code 1 — no exception; the loop reads 7 words past the 1-element row.Notes
DOTNET_JitCloneLoops=0restores the exception.optCanOptimizeByLoopCloningchecks only therows/klocals for invariance, never the heap cellrows[k], so the preheaderrows[k].Lengthguard is stale.The store variant (
rows[k][i] = vwithnew int[1],n = 32) writes 31 words past the row and corrupts the GC heap:Assert failure: m_alignpad == 0inObjHeader::Validate(src/coreclr/vm/syncblk.cpp:1840).