Files
BT411/engine/MUNGA
Joe DiPrimaandClaude Fable 5 ac7c46b87a #93: fix the end-of-round PerformAndWatch crash -- the STATIC projectile
pool held dangling entity pointers across round teardown

Root cause (disasm-pinned on the shipped 4.11.659 exe): RajelAran's crash
(call to 0x65676769 = ASCII 'igge', EBP-walk return btl4+0x2b91a) is the
tgt->Dispatch vtable call in BTUpdateProjectiles' NON-MECH impact branch
(mech4.cpp:1598; the site matches the disasm literally -- the 0x12/0x64
TakeDamageMessage ctor, the EntityID::Null ternary, the getenv gate after).

The trap: the mech branch is guarded by BTIsRegisteredMech(tgt) -- but at
round teardown a destroyed mech is DEREGISTERED, so a round still in
flight (the pool is a static array that outlives the round; missiles are
slow, #84) holding it as p.target now FAILS the mech check and falls into
the !BTIsRegisteredMech branch, which treats the freed mech as a cultural
icon and dispatches into freed memory.  The liveness check itself routed
the dangling pointer into the unguarded branch.  Freed heap reused by a
string -> vtable slot +0x10 read 'igge' -> call 0x65676769.  (The EBP
walker explains the stack shape: the faulting call pushed its return
address on ESP, but the walk reads [EBP+4] = PerformAndWatch's frame.)

Fix -- scrub at the source of truth:
- BTProjectilesDropEntity(e): every pool entry drops a dying entity from
  p.target/p.shooter; called from ~Mech and ~CulturalIcon (the only free
  paths for targetable entities).  Flight + impact code is already
  null-guarded on both fields.
- BTProjectilesClearAll(): kills all rounds at RunMissions exit (cross-
  mission hygiene for the static pool).

Verified: 6 short-mission cycles (45s missions with a spawned dummy +
autofire, mission expiring mid-combat) -- 0 exceptions, 6/6 clean
'RunMissions returned'.  The original crash was a heap-reuse race with no
deterministic repro; the scrub eliminates the dangling-pointer class by
construction.  (Bench note: blind autofire never launches missiles -- the
launcher needs a target lock -- so the exact in-flight race was not
re-created; the non-regression + the pinned mechanism carry the verdict.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Zh7PTkFy4KwTzVighLR9J
2026-07-31 10:19:24 -05:00
..