Gauge: FIX CoolingLoopConnection AV -- guard the databinding-trap raw reads

BT_DEV_GAUGES crashed a few ticks in: CoolingLoopConnection::Update
(btl4gau2.cpp) walks the 1995 binary's plug->link->subsystem chain via
raw +8 offsets, but our reconstructed subsystem layout is not
byte-identical, so an intermediate 'link' comes out non-null-but-garbage
and *(link+8) AVs.  Fires the moment a mech's coolant loop goes active
(source+0x134==1) -- so it would crash the REAL POD too, not just the
dev composite; the brief earlier gauge runs just never activated the
loop.  Pre-existing (byte-identical across the BT411 merge), not a merge
regression.

Guard each raw deref (BTPtrReadable via VirtualQuery) so an invalid
chain resolves to the authentic 'no source' branch (0) instead of
faulting. [T3] -- proper fix is named Plug/Link accessors in a
complete-type TU; logged in the KB.

Verified: BT_DEV_GAUGES now survives coolant-loop activation and the
full cockpit MFD suite renders (Heat / weapon-status MFDs / radar /
Comm pilotList).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-16 19:46:29 -05:00
co-authored by Claude Fable 5
parent 503536f1b3
commit cefbc3a686
+35 -4
View File
@@ -127,6 +127,33 @@ static void *AttributePointerOf(void *subsystem, const char *name)
return p;
}
//
// BT412 DATABINDING GUARD [T3]: is `p` a readable committed address? The
// plug->link->subsystem chain below walks raw +8 offsets recovered from the
// 1995 binary, but our reconstructed subsystem layout is NOT byte-identical,
// so an intermediate "link" can come out non-null-but-garbage -- dereferencing
// it AVs (observed: CoolingLoopConnection::Update crash the moment a mech's
// coolant loop goes active, on the pod AND the dev composite). A readable-page
// check makes an invalid link resolve to the authentic "no source" branch (0)
// instead of faulting. Proper fix (follow-up): route through named Plug/Link
// accessors in a complete-type TU instead of raw +8 reads.
//
static bool BTPtrReadable(const void *p)
{
if (p == 0)
return false;
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery(p, &mbi, sizeof(mbi)) == 0)
return false;
if (mbi.State != MEM_COMMIT)
return false;
const DWORD readable = PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ
| PAGE_EXECUTE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY;
if ((mbi.Protect & readable) == 0)
return false;
return (mbi.Protect & PAGE_GUARD) == 0;
}
//
// FUN_00417ab4 -- SubsystemConnection::Resolve: follow the plug's bound link to the
// resolved subsystem. The link is `*(plug+8)`; the resolved subsystem is
@@ -136,10 +163,10 @@ static void *AttributePointerOf(void *subsystem, const char *name)
//
static void *ResolveLink(void *plug)
{
if (plug == 0)
if (!BTPtrReadable((char *)plug + 8))
return 0;
void *link = *(void **)((char *)plug + 8);
if (link == 0)
if (!BTPtrReadable((char *)link + 8)) // [T3] guard: layout-mismatch garbage link
return 0;
return *(void **)((char *)link + 8);
}
@@ -204,13 +231,17 @@ public:
void Update() // @004c31a0
{
void *resolved = 0;
if (source != 0 && *(int *)((char *)source + 0x134) == 1)
// [T3] guard the raw +0x134 / +0x1d4 reads against the layout mismatch
// (see BTPtrReadable above) -- an invalid chain reads "no source" (0).
if (BTPtrReadable((char *)source + 0x134) &&
*(int *)((char *)source + 0x134) == 1)
{
void *linked = FindLinkedSubsystem(source, 3);
if (linked != 0)
resolved = ResolveLink(linked);
}
*destination = (resolved == 0) ? 0 : *(int *)((char *)resolved + 0x1d4);
*destination = (resolved != 0 && BTPtrReadable((char *)resolved + 0x1d4))
? *(int *)((char *)resolved + 0x1d4) : 0;
}
protected:
void *source; // @0x10 this[4]