Skip to content

[Breaking change]: Disallow loading non-Explicit types with explicit field offsets #48897

@jkoritzinsky

Description

@jkoritzinsky

Breaking Change: Disallow loading non-Explicit types with explicit field offsets

Description

Starting in .NET 10 Preview 4, the .NET runtime enforces stricter validation for type layouts. Specifically, the runtime now throws a TypeLoadException if a type with a non-explicit layout (e.g., Auto or Sequential) specifies explicit field offsets using the [FieldOffset] attribute. This change aligns the runtime's behavior with the ECMA-335 specification, which only permits explicit field offsets for types with an Explicit layout.

Previously, the runtime ignored explicit field offsets on non-explicit layout types, which could lead to unexpected behavior or incorrect assumptions about memory layout.

This change was introduced in PR #113500.

Version

This breaking change was introduced in .NET 10 Preview 4.

Previous behavior

In earlier versions of .NET, the runtime ignored explicit field offsets on types with Auto or Sequential layouts. For example:

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
    [FieldOffset(0)] // Ignored in .NET versions prior to 10 Preview 4
    public int Field1;

    [FieldOffset(4)] // Ignored in .NET versions prior to 10 Preview 4
    public int Field2;
}

class Program
{
    static void Main()
    {
        Console.WriteLine("Struct loaded successfully.");
    }
}

The above code would execute without error, and the [FieldOffset] attributes would be ignored.

New behavior

Starting in .NET 10 Preview 4, the runtime enforces the ECMA-335 specification and throws a TypeLoadException if a type with Auto or Sequential layout specifies explicit field offsets. The following code will now throw an exception:

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
    [FieldOffset(0)] // Causes a TypeLoadException in .NET 10 Preview 4 and later
    public int Field1;

    [FieldOffset(4)] // Causes a TypeLoadException in .NET 10 Preview 4 and later
    public int Field2;
}

class Program
{
    static void Main()
    {
        Console.WriteLine("Struct loaded successfully.");
    }
}

Exception thrown:

System.TypeLoadException: Explicit field offsets are not allowed on types with Sequential or Auto layout.

Type of breaking change

  • Binary incompatible: Existing binaries that define non-explicit layout types with explicit field offsets will fail to load.
  • Behavioral change: Existing binaries might behave differently at runtime due to the stricter validation.

Reason for change

This change was introduced to align the .NET runtime with the ECMA-335 specification, which explicitly disallows explicit field offsets on types with Auto or Sequential layouts. The previous behavior of ignoring these offsets was non-compliant and could lead to unexpected behavior or incorrect assumptions about memory layout.

Recommended action

To resolve this issue, developers should update their code to use LayoutKind.Explicit for types that specify explicit field offsets. For example:

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Explicit)] // Use Explicit layout
public struct MyStruct
{
    [FieldOffset(0)]
    public int Field1;

    [FieldOffset(4)]
    public int Field2;
}

class Program
{
    static void Main()
    {
        Console.WriteLine("Struct loaded successfully.");
    }
}

Alternatively, if explicit field offsets are not required, remove the [FieldOffset] attributes and rely on the default behavior of Sequential or Auto layout.

Affected APIs

This change does not directly affect specific APIs but impacts the runtime behavior of types defined with the following attributes:

  • [StructLayout(LayoutKind.Sequential)]
  • [StructLayout(LayoutKind.Auto)]
  • [FieldOffset]

Feature area

  • Interop

Additional resources

For further assistance, please contact .NET Breaking Change Notifications.

Metadata

Metadata

Assignees

Labels

🗺️ reQUESTTriggers an issue to be imported into Quest.breaking-changeIndicates a .NET Core breaking change

Type

No type

Projects

Status

🔖 Ready

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions