Skip to content

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 #133757

Description

@EgorBo

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions