Skip to content

Guard item.original write in HookingSystem::Detach#145

Open
JustinMDotNet wants to merge 1 commit into
wopss:masterfrom
JustinMDotNet:fix/hooking-system-detach-null-original
Open

Guard item.original write in HookingSystem::Detach#145
JustinMDotNet wants to merge 1 commit into
wopss:masterfrom
JustinMDotNet:fix/hooking-system-detach-null-original

Conversation

@JustinMDotNet

Copy link
Copy Markdown

Closes #136.

What this changes

HookingSystem::Detach now guards the write to item.original with a null check, the same way Attach already does:

if (item.original)
{
    *item.original = nullptr;
}
it = m_hooks.erase(it);

Why

HookingSystem::Attach treats aOriginal as optional (it only writes through it when it is non-null, see HookingSystem.cpp:64), and the v1 entry point in Funcs.cpp:40-44 only rejects null aTarget and aDetouraOriginal is allowed to be null. But the previous Detach did *item.original = nullptr; unconditionally, so any plugin that legitimately registered a hook without asking for the original trampoline would crash the game process the first time it removed that hook.

Reproducer:

sdk->hooking->Attach(handle, target, detour, nullptr);
sdk->hooking->Detach(handle, target);   // <-- AV inside HookingSystem

HookingSystem::Shutdown is not affected because it uses QueueForDetach + m_hooks.clear() and never writes through item.original.

What I tested

Local rebuild. Behavior change is limited to the aOriginal == nullptr path, which previously always crashed; for plugins that passed a non-null aOriginal the behavior is identical.

Alternative considered

Rejecting aOriginal == nullptr at the v1 boundary in Funcs.cpp::v1::Hooking::Attach. That is a contract change and breaks the symmetry with Attach's existing optional-out behavior, so I went with the guard. Happy to switch to the boundary check if you would prefer.

HookingSystem::Attach already treats aOriginal as optional and only
writes to it when it is non-null. The v1 entry point in Funcs.cpp
also lets plugins pass nullptr for the original-out pointer (only
aTarget and aDetour are required). HookingSystem::Detach, however,
unconditionally executed:

    *item.original = nullptr;

after a successful detach, so any plugin that registered a hook
without asking for the original trampoline would crash the game
process the moment it removed that hook.

Mirror Attach's check by only writing through item.original when it
is non-null. Shutdown is not affected because it uses
QueueForDetach + m_hooks.clear() and never writes through
item.original.

Fixes wopss#136

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HookingSystem::Detach dereferences a possibly-null item.original

2 participants