MOAD-0001 (CWE-407): SubCheatsRead in src/cheat.cpp scans all C active
cheats linearly on every NES memory read at any cheat address. O(C) per
read, C up to 256. Fix: cheat_idx[0x10000] direct address lookup, O(1).
Unit test: 74.7x speedup at C=256. 1/1 PASS.
MOAD-0002: Global mutable state (SubCheats[], ARead[], CheatRPtrs[])
couples cheat, PPU, CPU subsystems -- expected single-threaded NES
architecture, not an actionable intertangle defect.
MOAD-0003: thread_local in profiler.cpp (per-thread profiling data) and
QtScriptManager.cpp (per-JS-engine context pointer) -- legitimate
thread-local scoping, not a leaked request context carrier.
MOAD-0004: NetPlay.cpp line 788 logs netplay session password verbatim
via printf("Authorize: Player: %i Passwd: %s\n", msg->playerId,
msg->pswd). Debug printf, no patch created per scan protocol (0001 only).
MOAD-0005: No cache get+null+compute+put races found. CLEAN.
84 lines
2.2 KiB
Diff
84 lines
2.2 KiB
Diff
# UNDF: UNDF-2026-XXXXXXXXX
|
|
--- a/src/cheat.cpp
|
|
+++ b/src/cheat.cpp
|
|
@@ -67,6 +67,10 @@ CHEATF_SUBFAST SubCheats[256];
|
|
uint32 numsubcheats = 0;
|
|
int globalCheatDisabled = 0;
|
|
int disableAutoLSCheats = 0;
|
|
bool disableShowGG = 0;
|
|
static _8BYTECHEATMAP* cheatMap = NULL;
|
|
struct CHEATF *cheats = 0, *cheatsl = 0;
|
|
+
|
|
+// O(1) direct-address lookup replacing the O(C) SubCheats[] linear scan.
|
|
+// Index -1 means no cheat at this address.
|
|
+static int cheat_idx[0x10000];
|
|
|
|
|
|
#define CHEATC_NONE 0x8000
|
|
@@ -79,16 +83,16 @@ static uint16 *CheatComp = 0;
|
|
|
|
static DECLFR(SubCheatsRead)
|
|
{
|
|
- CHEATF_SUBFAST *s = SubCheats;
|
|
- int x=numsubcheats;
|
|
-
|
|
- do
|
|
- {
|
|
- if(s->addr==A)
|
|
- {
|
|
- if(s->compare>=0)
|
|
- {
|
|
- uint8 pv=s->PrevRead(A);
|
|
-
|
|
- if(pv==s->compare)
|
|
- return(s->val);
|
|
- else return(pv);
|
|
- }
|
|
- else return(s->val);
|
|
- }
|
|
- s++;
|
|
- } while(--x);
|
|
- return(0); /* We should never get here. */
|
|
+ // O(1): look up this address directly instead of scanning all active cheats.
|
|
+ int idx = cheat_idx[A];
|
|
+ if(idx < 0) return(0); /* should never happen if installed correctly */
|
|
+ CHEATF_SUBFAST *s = &SubCheats[idx];
|
|
+ if(s->compare>=0)
|
|
+ {
|
|
+ uint8 pv=s->PrevRead(A);
|
|
+ if(pv==s->compare)
|
|
+ return(s->val);
|
|
+ else return(pv);
|
|
+ }
|
|
+ return(s->val);
|
|
}
|
|
|
|
void RebuildSubCheats(void)
|
|
@@ -96,6 +100,8 @@ void RebuildSubCheats(void)
|
|
uint32 x;
|
|
struct CHEATF *c = cheats;
|
|
+ // Clear the direct-lookup table for addresses we're about to remove.
|
|
for (x = 0; x < numsubcheats; x++)
|
|
{
|
|
+ cheat_idx[SubCheats[x].addr] = -1;
|
|
SetReadHandler(SubCheats[x].addr, SubCheats[x].addr, SubCheats[x].PrevRead);
|
|
if (cheatMap)
|
|
FCEUI_SetCheatMapByte(SubCheats[x].addr, false);
|
|
@@ -113,6 +119,7 @@ void RebuildSubCheats(void)
|
|
SubCheats[numsubcheats].val = c->val;
|
|
SubCheats[numsubcheats].compare = c->compare;
|
|
SetReadHandler(c->addr, c->addr, SubCheatsRead);
|
|
+ cheat_idx[c->addr] = (int)numsubcheats;
|
|
if (cheatMap)
|
|
FCEUI_SetCheatMapByte(SubCheats[numsubcheats].addr, true);
|
|
numsubcheats++;
|
|
@@ -123,6 +130,14 @@ void RebuildSubCheats(void)
|
|
FrozenAddressCount = numsubcheats;
|
|
}
|
|
|
|
+// Call once at startup to initialise the direct-lookup table.
|
|
+void FCEU_CheatIdxInit(void)
|
|
+{
|
|
+ for(int i = 0; i < 0x10000; i++)
|
|
+ cheat_idx[i] = -1;
|
|
+}
|