dotnet / dotnet/runtime

JIT: (bug) Loop cloning treats a jagged-array row `a[k]` as loop-invariant, dropping bounds checks when the row is replaced inside the loop

Open
#133,757 2 comments 0 reactions 1 assignee Claimed by @AndyAyersMS View on GitHub
area-CodeGen-coreclr
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

### Minimal repro
```csharp
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 @ b44cd904110)

### 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`).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.