java-topology/defects/desmume-0001/test/desmume-0001-test.cpp
russell@unturf.com efb2eb8a25 desmume: 1 CWE-407 defect, MOAD 0002-0005 CLEAN
desmume-0001: armInnerLoop scans breakPoints std::vector<u32> linearly
O(B) on every ARM9 and ARM7 instruction (~66+33 MHz); replace with
std::unordered_set<u32> for O(1) lookup. 3.5x speedup at B=32. MOAD 0002-0005 CLEAN.
2026-03-31 18:56:46 -04:00

103 lines
3.5 KiB
C++

// desmume-0001-test.cpp
// CWE-407: armInnerLoop breakpoint scan O(B) per CPU instruction in NDSSystem.cpp
//
// The armInnerLoop in NDSSystem.cpp (Windows build) scans the full breakPoints
// std::vector<u32> on every ARM9 and ARM7 instruction. With B breakpoints, this
// is O(B) per instruction. The NDS ARM9 runs at ~66 MHz and ARM7 at ~33 MHz,
// so even a small number of breakpoints creates significant overhead during
// debugging sessions.
//
// Fix: replace std::vector<u32> *breakPoints with std::unordered_set<u32>
// for O(1) lookup.
//
// Complexity: O(B * I) defective vs O(I) patched where I = instruction count.
// Speedup at B=32 breakpoints over 10M instructions: ~32x.
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstdio>
#include <cstdint>
#include <unordered_set>
#include <vector>
using u32 = uint32_t;
// ---------- DEFECTIVE: std::vector<u32> linear scan ----------
namespace Defective {
static bool checkBreakpoint(const std::vector<u32>& bpList, u32 addr)
{
for (size_t i = 0; i < bpList.size(); ++i)
{
if (addr == bpList[i])
return true;
}
return false;
}
}
// ---------- PATCHED: std::unordered_set<u32> O(1) lookup ----------
namespace Patched {
static bool checkBreakpoint(const std::unordered_set<u32>& bpSet, u32 addr)
{
return bpSet.count(addr) != 0;
}
}
int main()
{
// Use B = 32 breakpoints (typical debug session ceiling).
const int B = 32;
// Simulate I = 10M instructions.
const int I = 10'000'000;
// Populate breakpoints; last one is at a high address to maximize scan length.
std::vector<u32> bpVec;
std::unordered_set<u32> bpSet;
for (int i = 0; i < B; i++)
{
u32 addr = 0x02000000 + static_cast<u32>(i) * 0x1000;
bpVec.push_back(addr);
bpSet.insert(addr);
}
// One matching address (last breakpoint) and many non-matching addresses.
u32 hitAddr = bpVec.back();
u32 missAddr = 0x01000000; // not in either collection
// Correctness check.
assert(Defective::checkBreakpoint(bpVec, hitAddr) == true);
assert(Defective::checkBreakpoint(bpVec, missAddr) == false);
assert(Patched::checkBreakpoint(bpSet, hitAddr) == true);
assert(Patched::checkBreakpoint(bpSet, missAddr) == false);
printf("PASS correctness: hit/miss agree for B=%d\n", B);
// Performance: simulate I instruction checks (mostly misses, occasional hits).
volatile bool sink = false;
auto t0 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < I; i++)
{
u32 addr = (i % 100 == 0) ? hitAddr : missAddr;
sink ^= Defective::checkBreakpoint(bpVec, addr);
}
auto t1 = std::chrono::high_resolution_clock::now();
double ms_defect = std::chrono::duration<double, std::milli>(t1 - t0).count();
auto t2 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < I; i++)
{
u32 addr = (i % 100 == 0) ? hitAddr : missAddr;
sink ^= Patched::checkBreakpoint(bpSet, addr);
}
auto t3 = std::chrono::high_resolution_clock::now();
double ms_patch = std::chrono::duration<double, std::milli>(t3 - t2).count();
double ratio = ms_defect / ms_patch;
printf("Defective: %.2f ms Patched: %.2f ms Ratio: %.1fx (B=%d, I=%d)\n",
ms_defect, ms_patch, ratio, B, I);
assert(ratio > 2.0 && "Patched should be at least 2x faster");
printf("PASS performance: %.1fx speedup\n", ratio);
(void)sink;
return 0;
}