Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeLoadException when a ref struct using explicit layout has fields of type ref struct #111260

Open
lithiumtoast opened this issue Jan 10, 2025 · 6 comments · May be fixed by #111584
Open

TypeLoadException when a ref struct using explicit layout has fields of type ref struct #111260

lithiumtoast opened this issue Jan 10, 2025 · 6 comments · May be fixed by #111584
Labels
area-TypeSystem-coreclr in-pr There is an active PR which will close this issue when it is merged
Milestone

Comments

@lithiumtoast
Copy link

lithiumtoast commented Jan 10, 2025

Description

Context: Found in .NET 9 when experimenting with ref struct in P/Invoke scenarios. I want to make it clear that I'm not using "ref fields" but rather only "ref structs"; the fields of the struct are not ref (pointer) but the type is of ref struct (stack-only).

Reproduction Steps

using System;
using System.Runtime.InteropServices;
					
public class Program
{
    [StructLayout(LayoutKind.Explicit, Size = 8, Pack = 8)]
    public ref struct Foo
    {
        [FieldOffset(0)]
        public Bar A;
        [FieldOffset(4)]
        public Bar B;
    }

    public ref struct Bar
    {
        public uint Value;
    }
	
    public static void Main()
    {
	var e = default(Foo);
	e.A.Value = 1;
	e.B.Value = 2;
	Console.WriteLine("1 + 2 = " + (e.A.Value + e.B.Value));
    }
}

Expected behavior

A ref struct of explicit layout can have fields that are positioned using FieldOffset attribute where the type of the field is a ref struct without creating a runtime exception.

Actual behavior

When a ref struct is using explicit struct layout, a System.TypeLoadException can occur regarding the field offset of the struct's field which the type is another ref struct.

Unhandled exception. System.TypeLoadException: Could not load type 'Foo' from assembly 'r2oeekw4.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field.
   at Program.Main()

Regression?

No response

Known Workarounds

  • Removing the ref from the nested struct.

Configuration

  • .NET 9
  • macOS Ventura 13.6.5
  • Apple M1 (osx-arm64)
  • Not specific to any configuration.

Other information

Hypothesis: The word ref in the context of a struct (stack-only) is confused to be in the context of a field (pointer); the field is mistaken for an object type with rules about field alignment when it's actually a struct.

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Jan 10, 2025
@jkotas
Copy link
Member

jkotas commented Jan 10, 2025

The offending imprecise check was introduced in dotnet/coreclr#25200 .

@AaronRobinsonMSFT
Copy link
Member

AaronRobinsonMSFT commented Jan 19, 2025

@jkotas or @davidwrighton My interpretation of this scenario is that a ByRefLike value type should just be treated as any other value type. The ByRefLike nature of it doesn't impact the overlap or alignment rules.

My plan here would be to remove the alignment check for the ByRefLike type and let the "value type layout" check work for the ByRefLike type "as is". Is there a flaw with that approach?

CoreCLR:

if (pByValueMT->IsByRefLike() || pByValueMT->ContainsGCPointers())
{
if ((pFD->GetOffset() & ((ULONG)TARGET_POINTER_SIZE - 1)) != 0)
{
// If we got here, then a ByRefLike valuetype or a valuetype containing an OREF was misaligned.
badOffset = pFD->GetOffset();
fieldTrust.SetTrust(ExplicitFieldTrust::kNone);
break;
}

native AOT:

bool needsToBeAligned =
!computedOffset.IsIndeterminate
&&
(
fieldType.IsGCPointer
|| fieldType.IsByRefLike
|| (fieldType.IsValueType && ((DefType)fieldType).ContainsGCPointers)
);

@davidwrighton
Copy link
Member

We need an alignment check on the actual byref fields... If we remove the check on byref like types as a whole, we need to preserve that detail for them.

@AaronRobinsonMSFT
Copy link
Member

We need an alignment check on the actual byref fields...

Agreed. Does IsByRefLike return true for both ByRefLike types and byref fields?

@AaronRobinsonMSFT
Copy link
Member

We need an alignment check on the actual byref fields...

Oh, perhaps you meant that since ref fields only exists on ByRefLike types the this logic was implied. It does seem we validate this now too though. I wrote some of it previously when ref fields were added. I also see this in native AOT.

@AaronRobinsonMSFT
Copy link
Member

@davidwrighton It looks like we are missing some check here.

we need to preserve that detail for them.

I think I see what you meant by this now. Thanks.

@AaronRobinsonMSFT AaronRobinsonMSFT linked a pull request Jan 19, 2025 that will close this issue
@dotnet-policy-service dotnet-policy-service bot added the in-pr There is an active PR which will close this issue when it is merged label Jan 19, 2025
@AaronRobinsonMSFT AaronRobinsonMSFT added this to the 10.0.0 milestone Jan 19, 2025
@AaronRobinsonMSFT AaronRobinsonMSFT removed the untriaged New issue has not been triaged by the area owner label Jan 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-TypeSystem-coreclr in-pr There is an active PR which will close this issue when it is merged
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants