From 5aaaac1ee3ec78b1b49e879968077e9ccbf4c913 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Tue, 31 Mar 2026 17:50:45 -0400 Subject: [PATCH] fbneo: 1 CWE-407 defect, MOAD 0002-0005 CLEAN; mame: update scan to all 5 MOADs CLEAN fbneo-0001: BurnDrvGetIndex O(N) linear scan over 24493 drivers -> O(1) unordered_map, 91x speedup at N=5000. Fix: hash index built at BurnLibInit, cleared at BurnLibExit. mame: confirmed CLEAN on all 5 MOADs (binary search for driver lookup, single-threaded). --- defects/fbneo-0001/patch/fbneo-0001.patch | 69 +++++++++ defects/fbneo-0001/test/test | Bin 0 -> 29112 bytes .../test/test_burn_drv_get_index.cpp | 139 ++++++++++++++++++ defects/fbneo-scan/MOAD-2-5-CLEAN.md | 27 ++++ defects/mame/scan/CLEAN.md | 45 ++++-- 5 files changed, 266 insertions(+), 14 deletions(-) create mode 100644 defects/fbneo-0001/patch/fbneo-0001.patch create mode 100755 defects/fbneo-0001/test/test create mode 100644 defects/fbneo-0001/test/test_burn_drv_get_index.cpp create mode 100644 defects/fbneo-scan/MOAD-2-5-CLEAN.md diff --git a/defects/fbneo-0001/patch/fbneo-0001.patch b/defects/fbneo-0001/patch/fbneo-0001.patch new file mode 100644 index 000000000..cb3a7216a --- /dev/null +++ b/defects/fbneo-0001/patch/fbneo-0001.patch @@ -0,0 +1,69 @@ +# UNDF: UNDF-2026-XXXXXXXXX +--- a/src/burn/burn.cpp ++++ b/src/burn/burn.cpp +@@ -1,6 +1,7 @@ + // Burn - Drivers module + + #include "version.h" ++#include + #include "burnint.h" + #include "timer.h" + //#include "burn_sound.h" // included in burnint.h +@@ -88,6 +89,9 @@ static char** pszShortName = NULL, ** pszFullNameA = NULL; + static wchar_t** pszFullNameW = NULL; + static char szBackupNameW[MAX_PATH * sizeof(wchar_t)]; + ++// Hash index from short name to driver array index for O(1) BurnDrvGetIndex. ++static std::unordered_map g_drvIndexMap; ++ + static INT32 BurnCheckMMXSupport() + { + #if defined (_X86_) && defined (_MSC_VER) +@@ -108,6 +112,8 @@ static void BurnGameListInit() + if (0 == nBurnDrvCount) return; + + pszShortName = (char** )malloc(nBurnDrvCount * sizeof(char*)); ++ g_drvIndexMap.clear(); ++ g_drvIndexMap.reserve(nBurnDrvCount); + pszFullNameA = (char** )malloc(nBurnDrvCount * sizeof(char*)); + pszFullNameW = (wchar_t**)malloc(nBurnDrvCount * sizeof(wchar_t*)); + +@@ -122,6 +128,7 @@ static void BurnGameListInit() + if (NULL != pszShortName[i]) { + strcpy(pszShortName[i], pDriver[i]->szShortName); + pDriver[i]->szShortName = pszShortName[i]; ++ g_drvIndexMap[pDriver[i]->szShortName] = (INT32)i; + } + if (NULL != pszFullNameA[i]) { + strcpy(pszFullNameA[i], pDriver[i]->szFullNameA); +@@ -149,6 +156,7 @@ static void BurnGameListExit() + if ((NULL != pszFullNameW) && (NULL != pszFullNameW[i])) free(pszFullNameW[i]); + } + ++ g_drvIndexMap.clear(); + if (NULL != pszShortName) free(pszShortName); + if (NULL != pszFullNameA) free(pszFullNameA); + if (NULL != pszFullNameW) free(pszFullNameW); +@@ -584,12 +592,13 @@ extern "C" wchar_t* BurnDrvGetFullNameW(UINT32 i) + return pDriver[i]->szFullNameW; + } + +-extern "C" INT32 BurnDrvGetIndex(char* szName) ++// BurnDrvGetIndex -- O(1) hash lookup, was O(N) linear scan over all drivers. ++extern "C" INT32 BurnDrvGetIndex(char* szName) // CWE-407 fix: unordered_map + { + if (NULL == szName) return -1; + +- for (UINT32 i = 0; i < nBurnDrvCount; i++) { +- if (0 == strcmp(szName, pDriver[i]->szShortName)) { +-// nBurnDrvActive = i; +- return i; +- } +- } +- +- return -1; ++ auto it = g_drvIndexMap.find(szName); ++ if (it != g_drvIndexMap.end()) ++ return it->second; ++ return -1; + } diff --git a/defects/fbneo-0001/test/test b/defects/fbneo-0001/test/test new file mode 100755 index 0000000000000000000000000000000000000000..0fe3254ad0f969c99a81dc3a1116d2253acfb14f GIT binary patch literal 29112 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 Ot*be9QI O(1) hash lookup. +// Models the defect: with 5000+ game drivers, every call to BurnDrvGetIndex +// (game load, state load, Kaillera/netplay) scanned the full driver array. +// Fix: build std::unordered_map at BurnLibInit time, O(1) lookup. + +#include +#include +#include +#include +#include +#include +#include + +// Minimal driver struct matching FBNeo's BurnDriver +struct BurnDriver { + const char* szShortName; +}; + +// Original: O(N) linear scan +static int drv_get_index_original(const std::vector& pDriver, + const char* szName) +{ + if (!szName) return -1; + for (int i = 0; i < (int)pDriver.size(); i++) { + if (strcmp(szName, pDriver[i]->szShortName) == 0) + return i; + } + return -1; +} + +// Patched: O(1) hash lookup +struct PatchedIndex { + std::unordered_map map; + + void build(const std::vector& pDriver) { + map.clear(); + map.reserve(pDriver.size()); + for (int i = 0; i < (int)pDriver.size(); i++) + map[pDriver[i]->szShortName] = i; + } + + int get(const char* szName) const { + if (!szName) return -1; + auto it = map.find(szName); + return (it != map.end()) ? it->second : -1; + } +}; + +int main() +{ + // Build driver pool modeling FBNeo's ~5000 game drivers + const int N_DRIVERS = 5000; + std::vector names; + names.reserve(N_DRIVERS); + for (int i = 0; i < N_DRIVERS; i++) + names.push_back("drv_" + std::to_string(i)); + + std::vector drivers(N_DRIVERS); + std::vector pDriver(N_DRIVERS); + for (int i = 0; i < N_DRIVERS; i++) { + drivers[i].szShortName = names[i].c_str(); + pDriver[i] = &drivers[i]; + } + + PatchedIndex idx; + idx.build(pDriver); + + // Test 1: found at index 0 (best case original, same patched) + { + int orig = drv_get_index_original(pDriver, "drv_0"); + int patched = idx.get("drv_0"); + assert(orig == 0); + assert(patched == 0); + printf("PASS: lookup first driver\n"); + } + + // Test 2: found at last index (worst case original) + { + int orig = drv_get_index_original(pDriver, "drv_4999"); + int patched = idx.get("drv_4999"); + assert(orig == 4999); + assert(patched == 4999); + printf("PASS: lookup last driver\n"); + } + + // Test 3: not found returns -1 + { + int orig = drv_get_index_original(pDriver, "no_such_game"); + int patched = idx.get("no_such_game"); + assert(orig == -1); + assert(patched == -1); + printf("PASS: missing driver returns -1\n"); + } + + // Test 4: null returns -1 + { + int orig = drv_get_index_original(pDriver, nullptr); + int patched = idx.get(nullptr); + assert(orig == -1); + assert(patched == -1); + printf("PASS: null name returns -1\n"); + } + + // Test 5: correctness across all drivers + { + for (int i = 0; i < N_DRIVERS; i += 100) { + assert(drv_get_index_original(pDriver, names[i].c_str()) == i); + assert(idx.get(names[i].c_str()) == i); + } + printf("PASS: correctness across all %d drivers\n", N_DRIVERS); + } + + // Test 6: performance -- lookup all N drivers once (covers entire range, no elision) + { + volatile int sink = 0; + + auto t0 = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < N_DRIVERS; i++) + sink += drv_get_index_original(pDriver, names[i].c_str()); + auto t1 = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < N_DRIVERS; i++) + sink += idx.get(names[i].c_str()); + auto t2 = std::chrono::high_resolution_clock::now(); + (void)sink; + + double orig_us = std::chrono::duration_cast(t1 - t0).count(); + double patch_us = std::chrono::duration_cast(t2 - t1).count(); + double ratio = orig_us / (patch_us > 0.0 ? patch_us : 1.0); + + printf("PASS: performance N=%d all-driver scan: original=%.0fus patched=%.0fus ratio=%.1fx\n", + N_DRIVERS, orig_us, patch_us, ratio); + // Scanning all N=5000 drivers takes O(N^2/2) comparisons vs O(N) hash lookups. + // Even with vectorization the ratio must exceed 5x at N=5000. + assert(ratio > 5.0); + } + + printf("ALL TESTS PASSED\n"); + return 0; +} diff --git a/defects/fbneo-scan/MOAD-2-5-CLEAN.md b/defects/fbneo-scan/MOAD-2-5-CLEAN.md new file mode 100644 index 000000000..2282d93c7 --- /dev/null +++ b/defects/fbneo-scan/MOAD-2-5-CLEAN.md @@ -0,0 +1,27 @@ +# FinalBurn Neo — MOAD 0002-0005 Scan: CLEAN + +**Date:** 2026-03-31 +**Scanner:** agent blackops +**Scope:** src/burn/, src/burner/, src/cpu/tms34010/ + +## MOAD-0002 (Intertangle): CLEAN (by design) + +`nBurnDrvActive` is a global index used both as "active driver cursor" and as a +temporary scan index. This is a known architectural pattern in FBNeo, not a runtime +coupling defect -- all driver enumeration uses guard/restore patterns. + +## MOAD-0003 (Leaked Context): CLEAN + +No `thread_local`, `__thread`, or ThreadLocal constructs found in src/. FBNeo is +single-threaded (emulation + UI on one thread). + +## MOAD-0004 (CWE-312 Logged Secret): CLEAN + +No credential, token, session password, or API key logging found. `bprintf` calls +log emulation state. Netplay (Kaillera) session handling does not log passwords. +Spectrum game descriptions include in-game passwords as ROM metadata -- not credentials. + +## MOAD-0005 (Thundering Herd): CLEAN + +Our cx4.cpp program cache (SNES coprocessor) is a 2-slot LRU. FBNeo is +single-threaded; no concurrent cache races are possible. diff --git a/defects/mame/scan/CLEAN.md b/defects/mame/scan/CLEAN.md index 6a3861329..a287c719a 100644 --- a/defects/mame/scan/CLEAN.md +++ b/defects/mame/scan/CLEAN.md @@ -1,25 +1,42 @@ -# MAME — CWE-407 Scan Result: CLEAN +# MAME — 5-MOAD Scan Result: CLEAN -**Date:** 2026-03-30 +**Date:** 2026-03-31 **Scanner:** agent blackops -**Scope:** src/emu, src/frontend, src/lib/util (sparse checkout) +**Scope:** src/emu/, src/frontend/mame/, src/devices/ (targeted grep) -## Methodology +## MOAD-0001 (CWE-407): CLEAN Searched for `std::find(` on vector/array containers inside loops, dedup patterns with `push_back`, visited-list membership, and linear container scans in hot paths. -## Findings +All `std::find` usages fall into safe categories: -All `std::find` usages in MAME fall into safe categories: - -1. **Constant-bounded containers:** Input type arrays (ioport.cpp), page mappings - (SCREEN_PAGE_NUM=16), parent chains (1-3 deep) +1. **Constant-bounded containers:** Input type arrays (ioport.cpp), page mappings, + parent chains (1-3 deep) 2. **Cycle detection on tiny chains:** Layout group nesting (rendlay.cpp), - software parent chains (romload.cpp) — depth < 5 -3. **Cold paths only:** Validation code (validity.cpp), info XML generation - (infoxml.cpp), CLI options (submenu.cpp) + software parent chains (romload.cpp) -- depth < 5 +3. **Cold paths only:** Validation code, info XML generation, CLI options 4. **Character/string searches:** Not container membership tests +5. **Driver lookup:** Uses a sorted `s_drivers_sorted[]` with binary search in + drivenum.cpp -- O(log N), no defect -MAME uses proper data structures (maps, sets, enumerators) for hot-path lookups. -No CWE-407 defects found. +## MOAD-0002 (Intertangle): CLEAN + +MAME uses a `running_machine` object that owns all subsystems. Device state is +encapsulated per-device. No shared mutable god-object coupling unrelated subsystems. +`g_profiler` is a profiling-only singleton with no behavioral coupling. + +## MOAD-0003 (Leaked Context): CLEAN + +No `thread_local` or `__thread` declarations found in src/emu/ or src/frontend/. +MAME is architecturally single-threaded (one machine per process). + +## MOAD-0004 (CWE-312 Logged Secret): CLEAN + +No credential, token, password, or API key logging found. `osd_printf_*` calls +log game state and configuration only. + +## MOAD-0005 (Thundering Herd): CLEAN + +Our only significant cache is `driver_enumerator::m_config` (machine_config LRU). +MAME is single-threaded; no concurrent cache population races are possible.