Skip to content

Game freezes and crash (access violation) when disabling my plugin with detours at runtime #280

@BebopSpikeSpiegel

Description

@BebopSpikeSpiegel

Description

I am not sure this was my side or sdk side, but can confirm to be reproduced on BebopSpikeSpiegel/NoMoreAltF4@d97ed8a

Disabling the plugin via the SDK overlay menu at runtime causes an access violation crash. The crash occurs immediately after the DLL is unloaded — the game tries to execute code at an address that was inside the now-freed plugin DLL.

Claude wrote the rest —

Steps to Reproduce

  1. Create a plugin that registers detours in OnEngineInitialized():
    void MyPlugin::OnEngineInitialized() {
        Hooks::ZAchievementManagerSimple_OnEventReceived->AddDetour(this, &MyPlugin::OnEventReceived);
        Hooks::ZAchievementManagerSimple_OnEventSent->AddDetour(this, &MyPlugin::OnEventSent);
        Hooks::Http_WinHttpCallback->AddDetour(this, &MyPlugin::Http_WinHttpCallback);
        Hooks::ZHttpResultDynamicObject_OnBufferReady->AddDetour(this, &MyPlugin::OnBufferReady);
    }
  2. Load the plugin, enter a mission (so hooks are actively being called)
  3. Open the SDK overlay menu (~ key) and uncheck the plugin to disable it
  4. Game crashes immediately

Crash Log

'HITMAN3.exe' (Win32): Unloaded 'D:\SteamLibrary\steamapps\common\HITMAN 3\Retail\mods\NoMoreAltF4.dll'
Exception thrown at 0x00007FF9D179BDB0 in HITMAN3.exe: 0xC0000005: Access violation executing location 0x00007FF9D179BDB0.

This is followed by hundreds of std::filesystem::filesystem_error exceptions, eventually leading to a stack overflow and process termination.

Analysis

The crash address (0x00007FF9D179BDB0) was inside the plugin DLL's code space before it was unloaded. After the DLL is freed from memory, something (likely the detour framework or a hooked game thread) still tries to jump to that address.

I initially tried calling RemoveDetoursWithContext(this) in the destructor, but this didn't help — the DLL is unloaded immediately after the destructor returns, and other threads may already have resolved function pointers into the DLL.

Looking at the sample mods (NoPause, AdvancedRating, FreeCam), none of them call RemoveDetoursWithContext in their destructors — they rely on the SDK framework to handle detour cleanup on plugin unload. This suggests the framework should be handling this automatically but isn't doing so safely when hooks are actively being called on other threads.

Expected Behavior

Disabling a plugin at runtime should safely remove all detours before unloading the DLL, ensuring no thread can jump into freed code.

Environment

  • ZHMModSDK: v4.0.0-rc.3
  • Game: HITMAN World of Assassination (latest Steam version)
  • OS: Windows 11 Pro
  • Hooks used: ZAchievementManagerSimple_OnEventReceived, ZAchievementManagerSimple_OnEventSent, Http_WinHttpCallback, ZHttpResultDynamicObject_OnBufferReady

Note

The v4.0.0-rc.3 release notes mention "Fixed game crash on editor unload" — this may be a similar issue affecting regular plugin unloads as well.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions