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
25 lines
789 B
Bash
25 lines
789 B
Bash
#!/usr/bin/env bash
|
|
# #93 bench: mission teardown with missiles IN FLIGHT (3 cycles).
|
|
set -x
|
|
cd /c/git/bt411/content || exit 1
|
|
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
|
sleep 2
|
|
for i in 1 2 3; do
|
|
rm -f td_$i.log
|
|
( export BT_PLATFORM=glass BT_START_INSIDE=1 BT_DEV_GAUGES=1 \
|
|
BT_LOG=td_$i.log BT_AFFINITY=0x03 \
|
|
BT_GOTO=enemy BT_GOTO_STOP=100 BT_AUTOFIRE=1 BT_PROJ_LOG=1 BT_SPAWN_ENEMY=1
|
|
../build/Release/btl4.exe -egg SHORTMSN.EGG &
|
|
wait )&
|
|
BGPID=$!
|
|
# mission length 45s + load ~20s + menu relaunch; give it 100s then kill the relaunch
|
|
sleep 100
|
|
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
|
sleep 2
|
|
echo "=== cycle $i ==="
|
|
grep -c "crash" td_$i.log
|
|
grep -c "RunMissions returned" td_$i.log
|
|
grep -c "projectile" td_$i.log
|
|
done
|
|
echo "=== DONE ==="
|