Issue #128 follow-on: bonus/camera GObj NULL-clears + ASan diagnostics#148
Merged
Issue #128 follow-on: bonus/camera GObj NULL-clears + ASan diagnostics#148
Conversation
Two more BSS-resident GObj* statics that survive scene transitions and get dereferenced as freed-arena memory on the next scene's first draw — same family as PR #119's mnplayersvs.c InitPlayer fix. Static analysis of the post-PR-119 codebase identified these as the most-likely remaining leakers in the issue #128 / sc1PBonusStageStartScene crash trace. Both fixes land in decomp (submodule bump): - gGRCommonStruct.bonus1/bonus2.interface_gobj — read by sc1PBonusStageUpdateTargetInterface / ...UpdatePlatformInterface via SObjGetStruct() before the next bonus stage's Make* functions write fresh values. NULL-cleared at the top of sc1PBonusStageInitVars. - gGMCameraGObj + gGMCameraStruct.{pzoom,pfollow}_fighter_gobj — the {pzoom,pfollow} fields are conditionally written by gmCameraSetZoomFighter/...SetFollowFighter; on scenes that don't enable those features, the prior fight's freed fighter_gobj survives and is read at lines 711-848 via DObjGetStruct/ftGetStruct. NULL-cleared at the top of gmCameraMakeDefaultCamera. Plus port-side ASan diagnostic infrastructure (also in decomp bump): - objman.c gcAppendGObjToDLLinkedList — poison-check this_gobj at the funnel for every write into gGCCommonDLLinks[]. Catches the injection AT THE CALLER, not at first-frame discovery, so ASan's use-after-free report stack names the function holding the stale BSS handle. - objdisplay.c gcCaptureTaggedGObjs — defensive poison-check at the iteration site, throttled. And one outer change: - port_watchdog.cpp — skip installing the SIGSEGV/SIGBUS handlers when __SANITIZE_ADDRESS__ is defined, so ASan's preinit handler wins the race and produces its full report (alloc/free traces, shadow map). SIGILL/FPE/ABRT remain ours; SIGABRT chains after ASan has already printed its report (abort_on_error=1 path). Decomp submodule bump: b47c28b59 → ffaa7e769 on port-patches Closes nothing definitive yet — keeps issue #128 open until the ASan diagnostics confirm the fix on a live repro. The two NULL-clear sites are defensive: they match the issue #128 failure shape and the PR #119 fix template, but the active reproducer hasn't been re-run post-fix to confirm.
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.
Summary
mnplayersvs.cInitPlayer fix, paste-mirrored to the 1P siblings two days ago.What changed
Decomp (submodule bump on
port-patches)decomp/src/sc/sc1pmode/sc1pbonusstage.c— cleargGRCommonStruct.bonus1.interface_gobjandbonus2.interface_gobjat the top ofsc1PBonusStageInitVars. These are read bysc1PBonusStageUpdateTargetInterface/...UpdatePlatformInterfaceviaSObjGetStruct()and written only insidesc1PBonusStageMakeTargets/...MakePlatforms. Any Update-before-Make path on a re-entry derefs the prior bonus stage's freed GObj.decomp/src/gm/gmcamera.c— cleargGMCameraGObjandgGMCameraStruct.{pzoom,pfollow}_fighter_gobjat the top ofgmCameraMakeDefaultCamera. The{pzoom,pfollow}fields are conditionally written (only bygmCameraSetZoomFighter/...SetFollowFighter); on scenes that don't enable zoom/follow, the prior fight's freed fighter_gobj survives. Reads at gmcamera.c:711-848 deref viaDObjGetStruct/ftGetStruct.decomp/src/sys/objman.c—__asan_region_is_poisonedcheck at the start ofgcAppendGObjToDLLinkedList(the funnel for every write intogGCCommonDLLinks[]). When a caller passes a stale GObj*, the diagnostic logs it, then the natural deref one line down trips ASan with a use-after-free report whose stack frame names the caller.decomp/src/sys/objdisplay.c— defensive poison-check at thegcCaptureTaggedGObjsiteration site, throttled to 8 reports per frame.Both diagnostics gated on
__SANITIZE_ADDRESS__/__has_feature(address_sanitizer). Zero impact on release builds. Pattern mirrorslibultraship/src/fast/interpreter.cpp:50-56.Port
port/port_watchdog.cpp— skip installing the SIGSEGV / SIGBUS handlers when ASan is compiled in. ASan's preinit handler then wins, and we get the full alloc/free trace instead of our register dump. SIGILL / FPE / ABRT stay ours (SIGABRT chains after ASan has already printed its report underabort_on_error=1).Decomp submodule bump
b47c28b59→ffaa7e769onport-patches. Two commits:Issue #128: NULL-clear bonus-stage and camera GObj BSS statics on scene initIssue #128: ASan diagnostics for stale GObj injection into gGCCommonDLLinks[]Background
Failure mode is documented at
docs/issue128_investigation_2026-05-03.md. Two reporter paths converge onsc1PBonusStageStartScene's first-drawgcCaptureTaggedGObjswalking a stale GObj ongGCCommonDLLinks[]:gfx_stepin libultraship Fast3D)PR #119's Lead 3 fix already covered the obvious VS-side and 1P-side
InitPlayerslot fields. PR #144's Lead 1 fix addedportPackedDisplayListCacheDeleteRange. Verified those are in place but the bug still reproduces — the BSS leak is in non-mnplayers/code. Static analysis (parallel sub-agent sweep overit/,sc/,gm/,gr/,mn/,mv/,ft/) flaggedgGRCommonStruct.bonus*.interface_gobjandgGMCameraGObj/pzoom/pfollowfields as the most-likely remaining leakers matching the issue #128 failure shape.Status
The two NULL-clears are defensive — they fix a real null-hygiene gap that matches the failure shape, but the active reproducer hasn't been re-run post-fix to confirm. The ASan diagnostic infrastructure ships alongside so the next ASan repro names the leaker definitively if these two fixes don't fully close the issue.
Test plan
gcAppendGObjToDLLinkedListwill name the remaining leaker for a follow-up PR🤖 Generated with Claude Code