Skip to content

[JIT] SIGSEGV on Linux when loading Nullable<T> where T is a readonly record struct with static abstract interface members #127696

@MilanLochovsky

Description

@MilanLochovsky

Description

[PrestubMethodFrame] SIGSEGV (signal 11) on Linux when the JIT compiles a method that causes type-loading of Nullable<T> where T is a readonly record struct implementing a recursive generic interface with static abstract members.

Works correctly on Windows with the same SDK/runtime version (10.0.7, commit b16286c228).

Reproduction Steps

Minimal repro project (net10.0, LangVersion preview, Nullable enable):

// Program.cs
var list = new List<MyNullableUser>();
list.Add(new MyNullableUser { UserId = default });
Console.WriteLine("OK");

class MyNullableUser
{
    public UserId? UserId { get; set; }  // triggers SIGSEGV on Linux
}

public interface IEntityId
{
    int ToInt();
}

public interface IEntityId<TSelf> : IEntityId
    where TSelf : IEntityId<TSelf>
{
    EntityIdValue<TSelf> Value { get; }
    static abstract int MaxValue { get; }
    static abstract TSelf FromInt(int value);
    int IEntityId.ToInt() => Value.Value;
}

public readonly record struct EntityIdValue<TId>
    where TId : IEntityId<TId>
{
    public int Value { get; }
    public EntityIdValue(int value)
    {
        if (value < 0 || value > TId.MaxValue)  // static abstract dispatch
            throw new ArgumentOutOfRangeException(nameof(value));
        Value = value;
    }
}

public readonly record struct UserId(EntityIdValue<UserId> Value) : IEntityId<UserId>
{
    public static int MaxValue => 600;
    public static UserId FromInt(int value) => new(new EntityIdValue<UserId>(value));
    public int CompareTo(UserId other) => Value.Value.CompareTo(other.Value.Value);
}
<!-- JitBugRepro.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <LangVersion>preview</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
</Project>

Run on Linux using the official image:

podman run --rm \
  -v $(pwd):/app \
  "mcr.microsoft.com/dotnet/sdk:10.0@sha256:8a90a473da5205a16979de99d2fc20975e922c68304f5c79d564e666dc3982fc" \
  bash -c "cd /app && dotnet run"

Expected behavior

Program prints OK and exits cleanly.

Actual behavior

Segmentation fault (core dumped)

Stack trace from dotnet-dump (captured from real-world reproduction in a larger application hitting the same pattern):

[PrestubMethodFrame] — method containing type-load of Nullable<UserId>

[PrestubMethodFrame] indicates the CLR JIT crashed while compiling the method for the first time. Signal: 11 (SIGSEGV).

Workaround: Change UserId?UserId (non-nullable). Application starts correctly on Linux with this change.

Regression?

No response

Known Workarounds

Avoid Nullable<T> where T implements an interface with static abstract members.
Use a non-nullable value type with a sentinel value (e.g. default(UserId) to represent "none").

Configuration

Linux (crashing):

Property Value
Container image mcr.microsoft.com/dotnet/sdk:10.0
Image digest sha256:8a90a473da5205a16979de99d2fc20975e922c68304f5c79d564e666dc3982fc
OS Ubuntu 24.04
.NET SDK 10.0.203 (commit c23858a6d8)
.NET Runtime 10.0.7 (commit b16286c228)
Architecture x64
RID linux-x64

Windows (working):

Property Value
OS Windows 11 Pro 10.0.26100
.NET SDK 10.0.101 (commit fad253f51b)
.NET Runtime 10.0.7 (commit b16286c228)
Architecture x64
RID win-x64

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions