dotnet / dotnet/runtime

JIT: (bug) runtime-async await of a null Task completes successfully instead of throwing NullReferenceException

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

Description

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

class P
{
[MethodImpl(MethodImplOptions.NoInlining)]
static Task GetNull(Task t) { t = null; return t; }

static async Task Test() => await GetNull(Task.FromResult(42));

static void Main()
{
try { Console.WriteLine("returned " + Test().GetAwaiter().GetResult()); }
catch (NullReferenceException) { Console.WriteLine("NullReferenceException"); }
}
}
```

**Expected:** `NullReferenceException` (awaiting a null `Task` must throw).
**Actual:** `returned 0`.

Needs runtime-async: compile with `csc -optimize+ -features:runtime-async=on`, run `corerun repro.dll`. Repros at tier0 and with `DOTNET_TieredCompilation=0`; prints `NullReferenceException` correctly when compiled `-optimize-` (the `starg`/`ldarg` pair is gone there).
`impFoldAwaitedTopOfStack` (src\coreclr\jit\importer.cpp:12180-12238) matches `STORE_LCL_VAR(t, null)` + `LCL_VAR(t)` via `IsIntegralConst(0)`, which a `TYP_REF` null constant satisfies, and folds the await to `default` without ever checking that the awaitable is a `ValueTask`. The non-generic `Task` case likewise completes normally instead of throwing.

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.