diff --git a/src/coreclr/vm/ilmarshalers.h b/src/coreclr/vm/ilmarshalers.h index ba07cd5b55c17..8d24d07500472 100644 --- a/src/coreclr/vm/ilmarshalers.h +++ b/src/coreclr/vm/ilmarshalers.h @@ -988,11 +988,19 @@ class ILMarshaler m_nativeHome.InitHome(ILStubMarshalHome::HomeType_ILByrefLocal, pcsSetup->NewLocal(nativeFieldTypeByRef), &nativeType, /* unalignedIndirectStore */ true); + // During the cleanup pass, the managed typed is explicitly passed as null - see DestroyStructure(). This works + // except if the type has nested non-blittable fields. Not checking for null will compute invalid offsets that + // can cause an access violation during the field clean-up. + ILCodeLabel *pSkipComputeOffsetLabel = pcsSetup->NewCodeLabel(); + pcsSetup->EmitNOP("// field setup {"); pcsSetup->EmitNOP("// managed field setup {"); pcsSetup->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); + pcsSetup->EmitDUP(); + pcsSetup->EmitBRFALSE(pSkipComputeOffsetLabel); pcsSetup->EmitLDC(managedOffset); pcsSetup->EmitADD(); + pcsSetup->EmitLabel(pSkipComputeOffsetLabel); EmitStoreManagedHomeAddr(pcsSetup); pcsSetup->EmitNOP("// } managed field setup"); pcsSetup->EmitNOP("// native field setup {"); diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/StructureToPtrTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/StructureToPtrTests.cs index f174ab1cb03f6..ea55d5c177c54 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/StructureToPtrTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/StructureToPtrTests.cs @@ -256,6 +256,30 @@ public unsafe void StructureToPtr_OpaqueStruct_In_NonBlittableStructure_Success( Assert.Equal(*opaqueData, *marshaledOpaqueData); } + [Fact] + public void StructureToPtr_Flat_And_Nested_NonBlittableStructure_Success() + { + MarshalAndDestroy(new NonBlittableStruct_Flat + { + del = null, + b = 0x55, + }); + + MarshalAndDestroy(new NonBlittableStruct_Nested + { + s = { del = null }, + b = 0x55, + }); + + static unsafe void MarshalAndDestroy(T value) where T : struct + { + int sizeof_T = Marshal.SizeOf(); + void* ptr = stackalloc byte[sizeof_T]; + Marshal.StructureToPtr(value, (IntPtr)ptr, false); + Marshal.DestroyStructure((IntPtr)ptr); + } + } + public struct StructWithIntField { public int value; @@ -318,5 +342,22 @@ public struct NonBlittableWithOpaque public OpaqueStruct opaque; public string str; } + + public struct NonBlittableStruct_Flat + { + public Delegate del; + public byte b; + } + + public struct NonBlittableStruct_Nested + { + public struct InnerStruct + { + public Delegate del; + } + + public InnerStruct s; + public byte b; + } } }