dotnet / dotnet/runtime

JIT: (bug) Wrong exception order for `stelem.ref`: the array range check runs before the value expression

Open
#133,860 1 comment 0 reactions 0 assignees View on GitHub
area-CodeGen-coreclr
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

For `stelem.ref` the value operand must be evaluated before the array store's range check. When morph
demotes the array-store helper call into a plain `STOREIND`, the range check ends up first and the wrong
exception is reported.

### Minimal Repro

```csharp
using System;
using System.Runtime.CompilerServices;

class C
{
public object F;
}

class Program
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static object[] Make(int len) => new object[len];

[MethodImpl(MethodImplOptions.NoInlining)]
static void Use(object[] a) { }

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static void Test(int len, int index, C c)
{
object[] arr = Make(len);
arr[index] = c.F; // 'c.F' must be evaluated before the store's range check
Use(arr);
}

static void Main()
{
try
{
Test(1, 5, null);
Console.WriteLine("no exception");
}
catch (Exception e)
{
Console.WriteLine(e.GetType().Name);
}
}
}
```

### Expected

```
NullReferenceException
```

### Actual

```
IndexOutOfRangeException
```

The bounds check precedes the faulting load of `c.F`:

```asm
cmp rcx, rdx
jae SHORT G_M62848_IG04 ; <-- range check first
lea rcx, bword ptr [rdi+8*rcx+0x10]
mov rdx, gword ptr [rbx+0x08] ; <-- 'c.F' (must fault first) second
call CORINFO_HELP_ASSIGN_REF
```

### Notes

The `CEE_STELEM_REF` helper-call path in `importer.cpp` does not apply the `ARR_ST` ("strict ordering of
exceptions for array store") spill that the inline `INDEX_ADDR` path applies. When `gtCanSkipCovariantStoreCheck`
later succeeds in `fgMorphCall` and rewrites the helper call into `STOREIND(INDEX_ADDR(arr, index), value)`,
that ordering guarantee is lost. Writing `new object[len]` directly (no inlined `Make`) takes the inline path
and behaves correctly. Also reproduces on .NET 10.0.12, so not a `main`-only regression.

Contributor guide

Open the contributing guide

Research direction

Reproduce the exception-ordering difference with the minimal C# program, then inspect importer.cpp around the CEE_STELEM_REF helper-call path and the ARR_ST spill used by INDEX_ADDR. Follow fgMorphCall and gtCanSkipCovariantStoreCheck to understand where the ordering is lost. Done means the repro reports NullReferenceException and the behavior is covered by a regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, csharp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.