Skip to content

AddVectoredExceptionHandler handler callback causes ExecutionEngineException #1253

@sards3

Description

@sards3

Actual behavior

Using CsWin32's AddVectoredExceptionHandler, when the handler callback is called by Windows, we get the message "Fatal error. Invalid Program: attempted to call a UnmanagedCallersOnly method from managed code." printed to the console, and the program crashes with an ExecutionEngineException.

Expected behavior

The handler callback should not crash the program. In the example below, I show one way of getting the handler callback to work using RuntimeMethodHandle.GetFunctionPointer(). I don't understand why the CsWin32 version (or the other approaches I show below) are not working though. Maybe I'm missing something?

Repro steps

  1. NativeMethods.txt content:
AddVectoredExceptionHandler
RemoveVectoredExceptionHandler
  1. NativeMethods.json content (if present):
    N/A

  2. Any of your own code that should be shared?

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.System.Diagnostics.Debug;

class Program
{
    public unsafe static void Main(string[] args)
    {
        if (!OperatingSystem.IsWindowsVersionAtLeast(5, 1, 2600))
            throw new InvalidOperationException();

        void* vehHandle;
        int x;

        // Comment/uncomment the following blocks to try different approaches.

        {
            // RuntimeMethodHandle.GetFunctionPointer: works as expected.
            var methodHandle = typeof(Program).GetMethod(nameof(MethodHandleVEH), BindingFlags.NonPublic | BindingFlags.Static)!.MethodHandle;
            RuntimeHelpers.PrepareMethod(methodHandle);
            // Note: this is not CsWin32's PInvoke; it's my own implementation. See below.
            vehHandle = AddVectoredExceptionHandler(1, methodHandle.GetFunctionPointer());
            Console.WriteLine($"MethodHandle handle: {(nint)vehHandle}");
            x = *(int*)0; // Should throw NullReferenceException.
            RemoveVectoredExceptionHandler(vehHandle);
        }

        {
            // CsWin32: ExecutionEngineException
            vehHandle = PInvoke.AddVectoredExceptionHandler(1, CsWin32VEH);
            Console.WriteLine($"CsWin32 handle: {(nint)vehHandle}");
            x = *(int*)0; // Should throw NullReferenceException, but there is an ExecutionEngineException first.
            PInvoke.RemoveVectoredExceptionHandler(vehHandle);
        }

        {
            // Unmanaged function pointer: ExecutionEngineException
            delegate* unmanaged<EXCEPTION_POINTERS*, int> funcPointer = &FunctionPointerVEH;
            vehHandle = AddVectoredExceptionHandler(1, (nint)funcPointer);
            Console.WriteLine($"Function Pointer handle: {(nint)vehHandle}");
            x = *(int*)0; // Should throw NullReferenceException, but there is an ExecutionEngineException first.
            RemoveVectoredExceptionHandler(vehHandle);
        }

        {
            // Marshal.GetFunctionPointerForDelegate: ExecutionEngineException
            var handlerDelegate = new VectoredExceptionHandler(DelegateVEH);
            vehHandle = AddVectoredExceptionHandler(1, Marshal.GetFunctionPointerForDelegate(handlerDelegate));
            Console.WriteLine($"Delegate handle: {(nint)vehHandle}");
            x = *(int*)0; // Should throw NullReferenceException, but there is an ExecutionEngineException first.
            RemoveVectoredExceptionHandler(vehHandle);
        }
    }

    static unsafe int MethodHandleVEH(EXCEPTION_POINTERS* e) => 0;
    static unsafe int CsWin32VEH(EXCEPTION_POINTERS* e) => 0;
    [UnmanagedCallersOnly] static unsafe int FunctionPointerVEH(EXCEPTION_POINTERS* e) => 0;
    static unsafe int DelegateVEH(EXCEPTION_POINTERS* e) => 0;

    [DllImport("Kernel32.dll")]
    static extern unsafe void* AddVectoredExceptionHandler(uint first, nint handlerAddress);

    [DllImport("Kernel32.dll")]
    static extern unsafe ulong RemoveVectoredExceptionHandler(void* handle);

    unsafe delegate int VectoredExceptionHandler(EXCEPTION_POINTERS* exceptionInfo);
}

Context

  • CsWin32 version: 0.3.106
  • Target Framework: net8.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions