Fix use-after-free in deferred drone projectile + health-reset callbacks#7
Open
brandons209 wants to merge 1 commit into
Open
Fix use-after-free in deferred drone projectile + health-reset callbacks#7brandons209 wants to merge 1 commit into
brandons209 wants to merge 1 commit into
Conversation
Two deferred callbacks dereference a wrapper whose backing handle can be freed before the deferred frame runs: - RocketHitOwningDronePost was passed the projectile's APersistentObject handle via RequestFrame. If the rocket is destroyed that frame (e.g. it spawns inside geometry/the drone and immediately detonates), OnEntityDestroyed frees the handle, but RequestFrame is not an SDKHook so it still fires next frame and rocket.WeaponLauncher dereferences a dead handle. Pass the entity index and re-resolve via GetEntityFromIndex (null once the entity leaves the manager), bailing if gone. Same idiom already used by OnRocketTouch/OnRocketEndTouch. - ResetPlayerHealth's resupply timer was passed the ADronePlayer handle. If the player disconnects during the 0.1s delay the AClient handle is freed, making GetPosition() a use-after-free. Pass the userid and re-resolve via GetClientOfUserId/GetClient, bailing if they left. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes two use-after-free crashes where a deferred callback dereferences a wrapper whose backing handle was freed before the callback ran. On a busy server these produced a high-volume
Invalid Handleexception storm (each writing a full stack trace to disk), which degraded server frame time.1.
RocketHitOwningDronePost(DroneWeapons.sp)OnRocketTouchqueuesRequestFrame(RocketHitOwningDronePost, rocket)passing the projectile'sAPersistentObjecthandle. If the rocket is destroyed that same frame — e.g. it spawns inside geometry / the drone and immediately detonates —OnEntityDestroyedfrees the handle, butRequestFrameis not an SDKHook, so it still fires next frame androcket.WeaponLauncherdereferences the freed handle.Fix: pass the entity index and re-resolve via
FEntityStatics.GetEntityFromIndex(returns null once the entity has left the manager), bailing if it's gone. This is the same index-resolve idiom already used inOnRocketTouch/OnRocketEndTouch.2.
ResetPlayerHealth(CustomDrones.sp)PlayerExitVehicle's resupply path queuesCreateTimer(0.1, ResetPlayerHealth, player)passing theADronePlayerhandle. If the player disconnects during the 0.1s window theAClienthandle is freed andplayer.GetPosition()is a use-after-free.Fix: pass the userid and re-resolve via
GetClientOfUserId+FEntityStatics.GetClient, bailing if they left.Verified
Reproduced on a dev server (rapid-fire rockets into geometry; disconnect during the resupply window). Pre-fix produced thousands of
Invalid Handleexceptions; post-fix zero, with the deferred callbacks safely bailing when the entity/client is gone.Related (not fixed here)
Separate, lower-frequency UAF during drone teardown:
ADrone.Destroy → FComponentArray.ClearComponents → FEntityStatics.DestroyEntity → EntNative_Destroy → APersistentObject.GetthrowsInvalid Handlewhen a component's handle was already freed (component entity destroyed independently while still in the drone's component array). There's no safe SourcePawn-level guard (every validity check dereferences the freed handle), so it needs an architectural fix — dropping a component from its parent drone'sAttachments/Weaponsarrays when its entity is destroyed. Flagging in case it's useful; happy to follow up.🤖 Generated with Claude Code