Marshalling C Struct as C # delegate return value

I am trying to return a small (8 byte) structure by value from a delegate associated with a native function, but when targeting .NET Framework 2.0, I run the following error (code seems to work correctly when targeting 4.0+):

An unhandled exception of type 'System.AccessViolationException' occurred in testclient.exe

Additional information: Attempt to read or write protected memory. This is often an indication that other memory is damaged.

I suspect I've messed up the managed type annotations so that the return value isn't sorted correctly, but I can't see what I'm doing wrong. Below is the code for a small test DLL and managed client that reproduces the problem.

C / C ++ Win32 (x86) test DLL

//Natural alignment, blittable, sizeof(StatusBlock) == 8
struct StatusBlock{
    std::uint32_t statusA;
    std::uint32_t statusB;
};

/*
 * When compiled this function stores the 64bit return value in the
 * eax:edx register pair as expected.
 */
static StatusBlock __cdecl SomeFunction(std::uint32_t const someVal){
    return StatusBlock{ someVal, 0x1234ABCD };
}

//Exported
extern "C" PVOID __stdcall GetFunctionPointer(){
    return &SomeFunction;
}

      

C # testing client

class Program
{

    //Blittable, Marshal.SizeOf(typeof(StatusBlock)) == 8
    [StructLayout(LayoutKind.Sequential)]
    private struct StatusBlock
    {
        public UInt32 statusA;
        public UInt32 statusB;
    }

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate StatusBlock SomeFunction(UInt32 someVal);

    [DllImport("testlib.dll",CallingConvention = CallingConvention.StdCall)]
    private static extern IntPtr GetFunctionPointer();

    static void Main(string[] args)
    {
        var fnPtr = GetFunctionPointer();

        System.Diagnostics.Debug.Assert(fnPtr != IntPtr.Zero);

        var someFn = (SomeFunction)Marshal.GetDelegateForFunctionPointer(fnPtr, typeof(SomeFunction));

        /* 
         * Crashes here with a System.AccessViolationException when targeting .NET Framework 2.0.
         * Works as expected when targeting .NET Framework 4.0 +
         */
        var statusBlock = someFn(22);
    }
}

      

It is worth noting that if the return type is a delegate Uint64

, then the application works in both .NET 2.0 and 4.0. However, I shouldn't have done that; StatusBlock

must be the correct marshal.

Have I had any luck setting up .NET 4.0? Any insight into what I am doing wrong would be greatly appreciated.

+3


source to share


1 answer


This is definitely a bug in .NET.

TL; DR

The .NET Framework 2 generates an invalid (possibly unsafe) stub.

How did I know about this?

I ran some tests:

  • Instead of an 8 byte long structure, I used a 4 byte long structure. He works!
  • Instead of using x86, I used x64. He works!

After finding out that it works in all other cases, I decided to disable it using windbg to see where it crashes (since Visual Studio won't let me "go" into native call

with a disassembly window).

Guess what I found: enter image description here

The .NET framework generated a stub calling memcpy

and it fails when it tries to copy to edi

, which at the time had a value of 0x16 (== 22), which was the parameter posted to the C # code!

So let's see what happens if I have to send a valid function pointer:

unsafe
{
    long* ptr = &something;
    uint ptr_value = (uint)ptr;
    Console.WriteLine("Pointer address: {0:X}", (long)ptr);

    var statusBlock = someFn(ptr_value);
    Console.WriteLine("A: {0}", statusBlock.statusA);

    Console.WriteLine("B: {0:X}", statusBlock.statusB);
}

      

Output: (it works and doesn't crash when a valid pointer is given!)



Marshal.SizeOf(typeof(StatusBlock)) = 8
Running .NET Version 2
Pointer address: 49F15C
A: 0
B: 0

      

So, I believe this is an invalid issue in the .NET Framework 2.

Why is this happening?

When a C function is defined to return struct

more than 8 bytes, the function must actually return a pointer to struct

in the local function stack

, and the caller must use memcpy

to copy it to it stack

(this is part of the C spec and is implemented by the compiler - the programmer just "returns" the structure, and the compiler does the heavy work).

However for 8 bytes (either struct

or long long

) most C compilers return it in eax:edx

. NET developers probably missed this. The error is probably that someone wrote size >= 8

instead of size > 8

...

Edit: what's worse, it writes the result at the specified pointer!

before: 0x1111222244445555
after : 0x1234ABCD007BEF5C

      

It changes the pointer to the return value! As you can see, the first dword

after the call is 0x1234ABCD (as in the native DLL) and the second dword

is a pointer to the value, that is, the parameter someVal

that was given!

This is even funnier, because if you pass a pointer to the structure StatusBlock

, it will actually work for this particular case (because the first dword

in the return value is used as a pointer)

Decision

Return the variable long

and create the structure yourself.

+3


source







All Articles