C
C#13mo ago
peppy

In a hooking/detouring scenario, "A callback was made on a garbage collected delegate of type (...)"

I am hooking a C++ application with C# functions using Detours. This, after another issue I ran into the other week (https://github.com/dotnet/runtime/issues/87188), works quite well. After a certain time (judging by the call stack, I assume the first GC), the application crashes with the call stack shown in the image and a pszMessage of A callback was made on a garbage collected delegate of type 'fhcshook!Fahrenheit.HookLib.FhHookDelegates+PrintfVarargDelegate::Invoke'.. How is the delegate being garbage collected? I store delegates in a static Dictionary field of a static class, as such:
public static class DelegateStore
{
public static readonly Dictionary<MethodBase, Delegate> Real = new Dictionary<MethodBase, Delegate>();
public static readonly Dictionary<MethodBase, Delegate> Mine = new Dictionary<MethodBase, Delegate>();
}
public static class DelegateStore
{
public static readonly Dictionary<MethodBase, Delegate> Real = new Dictionary<MethodBase, Delegate>();
public static readonly Dictionary<MethodBase, Delegate> Mine = new Dictionary<MethodBase, Delegate>();
}
and the relevant hook application code, abridged, is as follows:
// For some 'method' of type MethodBase and some 'dtype', the Type of the delegate
DelegateStore.Mine[method] = Delegate.CreateDelegate(dtype, method);

nint mbase = GetModuleHandle(mname);
if (mbase == nint.Zero) return;

nint addr = mbase + offset;

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(ref addr, Marshal.GetFunctionPointerForDelegate(DelegateStore.Mine[method]));
DetourTransactionCommit();

DelegateStore.Real[method] = Marshal.GetDelegateForFunctionPointer(addr, dtype);
// For some 'method' of type MethodBase and some 'dtype', the Type of the delegate
DelegateStore.Mine[method] = Delegate.CreateDelegate(dtype, method);

nint mbase = GetModuleHandle(mname);
if (mbase == nint.Zero) return;

nint addr = mbase + offset;

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(ref addr, Marshal.GetFunctionPointerForDelegate(DelegateStore.Mine[method]));
DetourTransactionCommit();

DelegateStore.Real[method] = Marshal.GetDelegateForFunctionPointer(addr, dtype);
I am well aware this is some failing to properly pin something movable/collectible, but what specifically? I find it hard to believe that DelegateStore is actually getting emptied out, so what else am I missing?
4 Replies
peppy
peppy13mo ago
If this is insufficient material to go on, I can share the entire project at your request.
however I thought that GetFunctionPointerForDelegate on a UnmanagedFunctionPointerAttribute with a reference was fine anyway.
Yeah, exactly. The delegate type I'm using here (PrintfVarargDelegate) is marked [UnmanagedFunctionPointer(CallingConvention.Cdecl)], Okay, it gets more confusing. If I run my hooks alone, as I normally do, everything works just fine. No problems, and definitely no crashes with randomly "garbage collected" delegates. That being said. For this particular application, there is another oft-used .NET application that runs beside it. It attaches to the process and twiddles a few knobs. It is only when it runs side-by-side with my hooks that I get this nonsense occurring. Which admittedly doesn't do much to explain what's going on for me, but I'll raise it with the relevant folks and see if they can clarify why the two would not be able to coexist.
peppy
peppy13mo ago
Discord
Discord - A New Way to Chat with Friends & Communities
Discord is the easiest way to communicate over voice, video, and text. Chat, hang out, and stay close with your friends and communities.
MODiX
MODiX13mo ago
fkelava#0000
In a situation where two .NET applications try to run "side-by-side" in a native process- one by way of .NET hosting, the other by attaching to the process later and Read/WriteProcessMemory'ing around- can one somehow cause side effects in the other? Because I'm getting some fairly obtuse errors specifically when both run at once that do not appear when either of them runs alone
Quoted by
<@!192550698515169281> from #advanced (click here)
React with ❌ to remove this embed.
Accord
Accord10mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.