dotnet / dotnet/runtime

JIT: (bug) `Enum.Equals` folding throws `NullReferenceException` instead of returning `false` for a `null` argument

Open
#133,817 1 comment 1 reaction 2 assignees Claimed by @EgorBo View on GitHub
area-CodeGen-coreclr
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

`Compiler::gtFoldExprCall` folds `NI_System_Enum_Equals` into a direct comparison of the two boxed payloads without checking that the *argument* is non-null, so a `null` argument is unconditionally dereferenced. `ValueType.Equals(object)` must return `false` for `null`, never throw.

### Minimal Repro

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

enum E : int { A = 0, B = 1 }

class Program
{
[MethodImpl(MethodImplOptions.NoInlining)]
static bool Test(E x, E? y)
{
object a = x;
object b = y; // null when y is null, but the JIT still sees exact class E
return a.Equals(b);
}

static void Main() => Console.WriteLine(Test(E.A, null));
}
```

### Expected

```
False
```

### Actual

```
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
at Program.Test(E x, Nullable`1 y)
```

The generated code has the `null` path (`xor rax, rax`) flowing straight into `cmp ecx, dword ptr [rax+0x08]`.

### Notes

The fold requires only an exact class handle for both operands and explicitly discards the `isNonNull` result (`bool isNonNull; // Unused here.`). Boxing a `Nullable` yields either a boxed `TEnum` (exact handle) or `null`, so exactness does not imply non-null. FullOpts only; MinOpts/tier-0 are correct. Does not repro on .NET 10 — regression from d3f4519befd ("Intrinsify Enum.Equals to avoid boxing", #122779).

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.