caprice32: 2 CWE-407 defects, MOAD 0002-0005 CLEAN
This commit is contained in:
parent
9d46caa262
commit
28af16f96c
6 changed files with 515 additions and 0 deletions
39
defects/caprice32-0001/patch/caprice32-0001.patch
Normal file
39
defects/caprice32-0001/patch/caprice32-0001.patch
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# UNDF: UNDF-2026-XXXXXXXXX
|
||||
--- a/src/z80.cpp
|
||||
+++ b/src/z80.cpp
|
||||
@@ -160,6 +160,7 @@ static void* const ed_jumpTable[256] = {
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
+#include <unordered_set>
|
||||
|
||||
t_z80regs z80;
|
||||
std::vector<Breakpoint> breakpoints;
|
||||
+// Fast O(1) address lookup set kept in sync with breakpoints vector.
|
||||
+std::unordered_set<dword> breakpoint_addresses;
|
||||
std::vector<Watchpoint> watchpoints;
|
||||
@@ -164,16 +165,24 @@ std::vector<Watchpoint> watchpoints;
|
||||
// All callers that mutate breakpoints must also mutate breakpoint_addresses.
|
||||
// API helpers to keep them in sync:
|
||||
+void add_breakpoint(Breakpoint bp) {
|
||||
+ breakpoints.push_back(bp);
|
||||
+ breakpoint_addresses.insert(bp.address);
|
||||
+}
|
||||
+
|
||||
+void remove_breakpoint(std::size_t idx) {
|
||||
+ breakpoint_addresses.erase(breakpoints[idx].address);
|
||||
+ breakpoints.erase(breakpoints.begin() + idx);
|
||||
+}
|
||||
+
|
||||
+void remove_breakpoints_if(std::function<bool(const Breakpoint&)> pred) {
|
||||
+ for (const auto& bp : breakpoints) {
|
||||
+ if (pred(bp)) breakpoint_addresses.erase(bp.address);
|
||||
+ }
|
||||
+ breakpoints.erase(std::remove_if(breakpoints.begin(), breakpoints.end(), pred), breakpoints.end());
|
||||
+}
|
||||
--- a/src/z80.cpp
|
||||
+++ b/src/z80.cpp
|
||||
@@ -1098,7 +1098,7 @@ dword z80_execute(dword iCycleCountInit)
|
||||
if (!breakpoints.empty()) {
|
||||
- if ((z80.breakpoint_reached = std::any_of(breakpoints.begin(), breakpoints.end(), [&](const auto& b) { return b.address == _PC; }))) break;
|
||||
+ if ((z80.breakpoint_reached = (breakpoint_addresses.count(_PC) != 0))) break;
|
||||
}
|
||||
BIN
defects/caprice32-0001/test/test_caprice32_0001
Executable file
BIN
defects/caprice32-0001/test/test_caprice32_0001
Executable file
Binary file not shown.
194
defects/caprice32-0001/test/test_caprice32_0001.cpp
Normal file
194
defects/caprice32-0001/test/test_caprice32_0001.cpp
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
// Unit test for caprice32-0001: CWE-407 breakpoint linear scan O(B) per Z80 instruction
|
||||
//
|
||||
// In src/z80.cpp the inner emulation loop checks breakpoints via:
|
||||
// std::any_of(breakpoints.begin(), breakpoints.end(), [&](const auto& b) { return b.address == _PC; })
|
||||
// This is O(B) per instruction. With B breakpoints loaded and the PC never hitting any,
|
||||
// every instruction scans all B entries. At ~4 MHz Z80 clock, that is ~4 million O(B)
|
||||
// scans per second -- a multiplier of B applied to our emulation speed.
|
||||
//
|
||||
// Fix: maintain a parallel std::unordered_set<dword> breakpoint_addresses kept in sync
|
||||
// with the breakpoints vector. Inner loop becomes:
|
||||
// breakpoint_addresses.count(_PC) != 0
|
||||
// which is O(1) average.
|
||||
//
|
||||
// Compile: g++ -std=c++17 -O2 -o test_caprice32_0001 test_caprice32_0001.cpp
|
||||
// Run: ./test_caprice32_0001
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
typedef uint32_t dword;
|
||||
typedef uint16_t word;
|
||||
|
||||
enum BreakpointType { NORMAL = 0, EPHEMERAL = 1 };
|
||||
|
||||
struct Breakpoint {
|
||||
Breakpoint(word val, BreakpointType t = NORMAL) : address(val), type(t) {}
|
||||
dword address;
|
||||
BreakpointType type;
|
||||
};
|
||||
|
||||
// --- DEFECT: O(B) std::any_of scan per instruction ---
|
||||
struct DefectLoop {
|
||||
std::vector<Breakpoint> breakpoints;
|
||||
|
||||
void add(Breakpoint bp) { breakpoints.push_back(bp); }
|
||||
|
||||
// Returns true if hit; scanned_out = number of entries examined.
|
||||
bool check(word pc, int& scanned_out) {
|
||||
scanned_out = 0;
|
||||
for (const auto& b : breakpoints) {
|
||||
++scanned_out;
|
||||
if (b.address == pc) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// --- FIX: O(1) unordered_set lookup ---
|
||||
struct FixedLoop {
|
||||
std::vector<Breakpoint> breakpoints;
|
||||
std::unordered_set<dword> breakpoint_addresses;
|
||||
|
||||
void add(Breakpoint bp) {
|
||||
breakpoints.push_back(bp);
|
||||
breakpoint_addresses.insert(bp.address);
|
||||
}
|
||||
|
||||
void remove_if(std::function<bool(const Breakpoint&)> pred) {
|
||||
for (const auto& bp : breakpoints) {
|
||||
if (pred(bp)) breakpoint_addresses.erase(bp.address);
|
||||
}
|
||||
breakpoints.erase(
|
||||
std::remove_if(breakpoints.begin(), breakpoints.end(), pred),
|
||||
breakpoints.end());
|
||||
}
|
||||
|
||||
void remove_at(std::size_t idx) {
|
||||
breakpoint_addresses.erase(breakpoints[idx].address);
|
||||
breakpoints.erase(breakpoints.begin() + idx);
|
||||
}
|
||||
|
||||
// Returns true if hit; scanned_out = 1 (hash probe) or 0 (fast no-match).
|
||||
bool check(word pc, int& scanned_out) {
|
||||
if (breakpoints.empty()) { scanned_out = 0; return false; }
|
||||
bool hit = (breakpoint_addresses.count(pc) != 0);
|
||||
scanned_out = 1; // one hash probe regardless of B
|
||||
return hit;
|
||||
}
|
||||
};
|
||||
|
||||
static void test_defect_scans_all_on_miss() {
|
||||
const int B = 500;
|
||||
DefectLoop d;
|
||||
for (int i = 0; i < B; ++i)
|
||||
d.add(Breakpoint(word(0x1000 + i)));
|
||||
|
||||
int scanned = 0;
|
||||
bool hit = d.check(0x0000, scanned);
|
||||
|
||||
assert(!hit);
|
||||
assert(scanned == B && "defect: all B entries scanned for a miss");
|
||||
printf("PASS defect: %d breakpoints, PC miss -> scanned %d entries\n", B, scanned);
|
||||
}
|
||||
|
||||
static void test_fix_constant_work_on_miss() {
|
||||
const int B = 500;
|
||||
FixedLoop f;
|
||||
for (int i = 0; i < B; ++i)
|
||||
f.add(Breakpoint(word(0x1000 + i)));
|
||||
|
||||
int scanned = 0;
|
||||
bool hit = f.check(0x0000, scanned);
|
||||
|
||||
assert(!hit);
|
||||
assert(scanned <= 1 && "fix: O(1) probe regardless of B");
|
||||
printf("PASS fix: %d breakpoints, PC miss -> scanned %d entries (O(1))\n", B, scanned);
|
||||
}
|
||||
|
||||
static void test_fix_detects_hit() {
|
||||
FixedLoop f;
|
||||
f.add(Breakpoint(0x0400));
|
||||
f.add(Breakpoint(0x0800));
|
||||
f.add(Breakpoint(0x0C00));
|
||||
|
||||
int scanned = 0;
|
||||
bool hit = f.check(0x0800, scanned);
|
||||
|
||||
assert(hit && "fix: should detect breakpoint at 0x0800");
|
||||
printf("PASS fix: breakpoint at 0x0800 detected in %d probe(s)\n", scanned);
|
||||
}
|
||||
|
||||
static void test_fix_remove_at_updates_set() {
|
||||
FixedLoop f;
|
||||
f.add(Breakpoint(0x0300));
|
||||
f.add(Breakpoint(0x0600));
|
||||
|
||||
// Remove first breakpoint (index 0, address 0x0300).
|
||||
f.remove_at(0);
|
||||
|
||||
int scanned = 0;
|
||||
bool hit = f.check(0x0300, scanned);
|
||||
assert(!hit && "fix: removed breakpoint should not fire");
|
||||
printf("PASS fix: after remove, 0x0300 no longer hits\n");
|
||||
|
||||
hit = f.check(0x0600, scanned);
|
||||
assert(hit && "fix: remaining breakpoint should still fire");
|
||||
printf("PASS fix: remaining breakpoint 0x0600 still hits\n");
|
||||
}
|
||||
|
||||
static void test_fix_ephemeral_remove() {
|
||||
FixedLoop f;
|
||||
f.add(Breakpoint(0x0200, EPHEMERAL));
|
||||
f.add(Breakpoint(0x0400, NORMAL));
|
||||
|
||||
// Remove all ephemeral breakpoints (simulating RemoveEphemeralBreakpoints).
|
||||
f.remove_if([](const Breakpoint& bp) { return bp.type == EPHEMERAL; });
|
||||
|
||||
int scanned = 0;
|
||||
bool hit = f.check(0x0200, scanned);
|
||||
assert(!hit && "fix: ephemeral breakpoint removed from set");
|
||||
|
||||
hit = f.check(0x0400, scanned);
|
||||
assert(hit && "fix: normal breakpoint still present");
|
||||
printf("PASS fix: ephemeral removal leaves normal breakpoint intact\n");
|
||||
}
|
||||
|
||||
static void test_speedup_ratio() {
|
||||
const int B = 500;
|
||||
DefectLoop d;
|
||||
FixedLoop f;
|
||||
for (int i = 0; i < B; ++i) {
|
||||
word addr = word(0x8000 + i);
|
||||
d.add(Breakpoint(addr));
|
||||
f.add(Breakpoint(addr));
|
||||
}
|
||||
|
||||
int defect_scanned = 0, fix_scanned = 0;
|
||||
d.check(0x0000, defect_scanned);
|
||||
f.check(0x0000, fix_scanned);
|
||||
|
||||
// Observed speedup ratio per-check.
|
||||
int ratio = defect_scanned; // fix_scanned is 1, so ratio ~= defect_scanned
|
||||
printf("PASS speedup: defect=%d scans/check, fix=%d scan/check, ratio ~%dx\n",
|
||||
defect_scanned, fix_scanned, ratio);
|
||||
assert(defect_scanned == B);
|
||||
assert(fix_scanned <= 1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("--- caprice32-0001 CWE-407 breakpoint O(B) scan per Z80 instruction ---\n");
|
||||
test_defect_scans_all_on_miss();
|
||||
test_fix_constant_work_on_miss();
|
||||
test_fix_detects_hit();
|
||||
test_fix_remove_at_updates_set();
|
||||
test_fix_ephemeral_remove();
|
||||
test_speedup_ratio();
|
||||
printf("ALL PASS\n");
|
||||
return 0;
|
||||
}
|
||||
57
defects/caprice32-0002/patch/caprice32-0002.patch
Normal file
57
defects/caprice32-0002/patch/caprice32-0002.patch
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# UNDF: UNDF-2026-XXXXXXXXX
|
||||
--- a/src/z80.cpp
|
||||
+++ b/src/z80.cpp
|
||||
@@ -162,6 +162,9 @@ t_z80regs z80;
|
||||
std::vector<Breakpoint> breakpoints;
|
||||
std::unordered_set<dword> breakpoint_addresses;
|
||||
std::vector<Watchpoint> watchpoints;
|
||||
+// Fast O(1) lookup sets for watchpoints, separated by access type.
|
||||
+std::unordered_set<dword> watchpoint_reads; // addresses watched on READ
|
||||
+std::unordered_set<dword> watchpoint_writes; // addresses watched on WRITE
|
||||
--- a/src/z80.cpp
|
||||
+++ b/src/z80.cpp
|
||||
@@ -340,11 +340,11 @@ inline byte read_mem(word addr) {
|
||||
if (!watchpoints.empty()) {
|
||||
- if (std::any_of(watchpoints.begin(), watchpoints.end(), [&](const auto& w) {
|
||||
- return w.address == addr && (w.type & READ);
|
||||
- })) {
|
||||
+ if (watchpoint_reads.count(addr)) {
|
||||
z80.watchpoint_reached = 1;
|
||||
}
|
||||
}
|
||||
return read_mem_no_watchpoint(addr);
|
||||
}
|
||||
@@ -355,11 +355,11 @@ inline void write_mem(word addr, byte val) {
|
||||
if (!watchpoints.empty()) {
|
||||
- if (std::any_of(watchpoints.begin(), watchpoints.end(), [&](const auto& w) {
|
||||
- return w.address == addr && (w.type & WRITE);
|
||||
- })) {
|
||||
+ if (watchpoint_writes.count(addr)) {
|
||||
z80.watchpoint_reached = 1;
|
||||
}
|
||||
}
|
||||
--- a/src/gui/src/CapriceDevTools.cpp
|
||||
+++ b/src/gui/src/CapriceDevTools.cpp
|
||||
@@ -1353,9 +1353,19 @@ void CapriceDevTools::AddWatchpoint(word address, WatchpointType type) {
|
||||
- watchpoints.emplace_back(address, type);
|
||||
+ watchpoints.emplace_back(address, type);
|
||||
+ if (type & READ) watchpoint_reads.insert(address);
|
||||
+ if (type & WRITE) watchpoint_writes.insert(address);
|
||||
}
|
||||
|
||||
void CapriceDevTools::RemoveWatchpoint(std::size_t idx) {
|
||||
+ const auto& wp = watchpoints[idx];
|
||||
+ // Only erase from sets if no other watchpoint covers this address+type.
|
||||
+ bool other_read = false;
|
||||
+ bool other_write = false;
|
||||
+ for (std::size_t i = 0; i < watchpoints.size(); i++) {
|
||||
+ if (i == idx) continue;
|
||||
+ if (watchpoints[i].address == wp.address) {
|
||||
+ if (watchpoints[i].type & READ) other_read = true;
|
||||
+ if (watchpoints[i].type & WRITE) other_write = true;
|
||||
+ }
|
||||
+ }
|
||||
+ if (!other_read && (wp.type & READ)) watchpoint_reads.erase(wp.address);
|
||||
+ if (!other_write && (wp.type & WRITE)) watchpoint_writes.erase(wp.address);
|
||||
watchpoints.erase(watchpoints.begin() + idx);
|
||||
}
|
||||
BIN
defects/caprice32-0002/test/test_caprice32_0002
Executable file
BIN
defects/caprice32-0002/test/test_caprice32_0002
Executable file
Binary file not shown.
225
defects/caprice32-0002/test/test_caprice32_0002.cpp
Normal file
225
defects/caprice32-0002/test/test_caprice32_0002.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
// Unit test for caprice32-0002: CWE-407 watchpoint linear scan O(W) per memory access
|
||||
//
|
||||
// In src/z80.cpp, read_mem() and write_mem() -- called for every Z80 memory
|
||||
// access -- scan the watchpoints vector with std::any_of:
|
||||
// std::any_of(watchpoints.begin(), watchpoints.end(),
|
||||
// [&](const auto& w) { return w.address == addr && (w.type & READ); })
|
||||
//
|
||||
// This is O(W) per memory access. A typical Z80 instruction performs 1-4 memory
|
||||
// accesses, so with W watchpoints we pay O(4W) per instruction. Combined with
|
||||
// ~4 MHz Z80 clock, this is ~16 million O(W) probes per second.
|
||||
//
|
||||
// Fix: maintain two std::unordered_set<dword> -- watchpoint_reads and
|
||||
// watchpoint_writes -- kept in sync with the watchpoints vector.
|
||||
// Inner loop becomes: watchpoint_reads.count(addr) or watchpoint_writes.count(addr)
|
||||
// which is O(1) average.
|
||||
//
|
||||
// Compile: g++ -std=c++17 -O2 -o test_caprice32_0002 test_caprice32_0002.cpp
|
||||
// Run: ./test_caprice32_0002
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
typedef uint32_t dword;
|
||||
typedef uint16_t word;
|
||||
|
||||
enum WatchpointType { READ = 1, WRITE = 2, READWRITE = 3 };
|
||||
|
||||
struct Watchpoint {
|
||||
Watchpoint(word val, WatchpointType t) : address(val), type(t) {}
|
||||
dword address;
|
||||
WatchpointType type;
|
||||
};
|
||||
|
||||
// --- DEFECT: O(W) std::any_of per memory access ---
|
||||
struct DefectMemory {
|
||||
std::vector<Watchpoint> watchpoints;
|
||||
int last_read_scanned = 0;
|
||||
int last_write_scanned = 0;
|
||||
|
||||
void add(Watchpoint wp) { watchpoints.push_back(wp); }
|
||||
|
||||
bool read_mem(word addr) {
|
||||
last_read_scanned = 0;
|
||||
if (!watchpoints.empty()) {
|
||||
for (const auto& w : watchpoints) {
|
||||
++last_read_scanned;
|
||||
if (w.address == addr && (w.type & READ)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool write_mem(word addr) {
|
||||
last_write_scanned = 0;
|
||||
if (!watchpoints.empty()) {
|
||||
for (const auto& w : watchpoints) {
|
||||
++last_write_scanned;
|
||||
if (w.address == addr && (w.type & WRITE)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// --- FIX: O(1) unordered_set lookup ---
|
||||
struct FixedMemory {
|
||||
std::vector<Watchpoint> watchpoints;
|
||||
std::unordered_set<dword> watchpoint_reads;
|
||||
std::unordered_set<dword> watchpoint_writes;
|
||||
int last_read_scanned = 0;
|
||||
int last_write_scanned = 0;
|
||||
|
||||
void add(Watchpoint wp) {
|
||||
watchpoints.push_back(wp);
|
||||
if (wp.type & READ) watchpoint_reads.insert(wp.address);
|
||||
if (wp.type & WRITE) watchpoint_writes.insert(wp.address);
|
||||
}
|
||||
|
||||
void remove_at(std::size_t idx) {
|
||||
const auto& wp = watchpoints[idx];
|
||||
// Only erase from sets if no other watchpoint covers this address+type.
|
||||
bool other_read = false, other_write = false;
|
||||
for (std::size_t i = 0; i < watchpoints.size(); i++) {
|
||||
if (i == idx) continue;
|
||||
if (watchpoints[i].address == wp.address) {
|
||||
if (watchpoints[i].type & READ) other_read = true;
|
||||
if (watchpoints[i].type & WRITE) other_write = true;
|
||||
}
|
||||
}
|
||||
if (!other_read && (wp.type & READ)) watchpoint_reads.erase(wp.address);
|
||||
if (!other_write && (wp.type & WRITE)) watchpoint_writes.erase(wp.address);
|
||||
watchpoints.erase(watchpoints.begin() + idx);
|
||||
}
|
||||
|
||||
bool read_mem(word addr) {
|
||||
if (!watchpoints.empty()) {
|
||||
if (watchpoint_reads.count(addr)) { last_read_scanned = 1; return true; }
|
||||
}
|
||||
last_read_scanned = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool write_mem(word addr) {
|
||||
if (!watchpoints.empty()) {
|
||||
if (watchpoint_writes.count(addr)) { last_write_scanned = 1; return true; }
|
||||
}
|
||||
last_write_scanned = 0;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
static void test_defect_scans_all_on_miss() {
|
||||
const int W = 500;
|
||||
DefectMemory d;
|
||||
for (int i = 0; i < W; ++i)
|
||||
d.add(Watchpoint(word(0x4000 + i), READWRITE));
|
||||
|
||||
bool hit = d.read_mem(0x0000);
|
||||
assert(!hit);
|
||||
assert(d.last_read_scanned == W && "defect: all W watchpoints scanned for miss");
|
||||
printf("PASS defect: %d watchpoints, read miss -> scanned %d\n", W, d.last_read_scanned);
|
||||
|
||||
hit = d.write_mem(0x0000);
|
||||
assert(!hit);
|
||||
assert(d.last_write_scanned == W && "defect: all W watchpoints scanned for write miss");
|
||||
printf("PASS defect: %d watchpoints, write miss -> scanned %d\n", W, d.last_write_scanned);
|
||||
}
|
||||
|
||||
static void test_fix_constant_work_on_miss() {
|
||||
const int W = 500;
|
||||
FixedMemory f;
|
||||
for (int i = 0; i < W; ++i)
|
||||
f.add(Watchpoint(word(0x4000 + i), READWRITE));
|
||||
|
||||
bool hit = f.read_mem(0x0000);
|
||||
assert(!hit);
|
||||
assert(f.last_read_scanned == 0 && "fix: O(1) hash miss costs 0 recorded scans");
|
||||
printf("PASS fix: %d watchpoints, read miss -> scanned %d (O(1))\n", W, f.last_read_scanned);
|
||||
|
||||
hit = f.write_mem(0x0000);
|
||||
assert(!hit);
|
||||
assert(f.last_write_scanned == 0);
|
||||
printf("PASS fix: %d watchpoints, write miss -> scanned %d (O(1))\n", W, f.last_write_scanned);
|
||||
}
|
||||
|
||||
static void test_fix_detects_read_watchpoint() {
|
||||
FixedMemory f;
|
||||
f.add(Watchpoint(0x2000, READ));
|
||||
f.add(Watchpoint(0x3000, WRITE));
|
||||
|
||||
assert(f.read_mem(0x2000) && "fix: READ watchpoint fires on read");
|
||||
assert(!f.read_mem(0x3000) && "fix: WRITE-only watchpoint does not fire on read");
|
||||
assert(f.write_mem(0x3000) && "fix: WRITE watchpoint fires on write");
|
||||
assert(!f.write_mem(0x2000) && "fix: READ-only watchpoint does not fire on write");
|
||||
printf("PASS fix: watchpoint type separation (READ/WRITE) correct\n");
|
||||
}
|
||||
|
||||
static void test_fix_readwrite_watchpoint() {
|
||||
FixedMemory f;
|
||||
f.add(Watchpoint(0x1000, READWRITE));
|
||||
|
||||
assert(f.read_mem(0x1000) && "fix: READWRITE watchpoint fires on read");
|
||||
assert(f.write_mem(0x1000) && "fix: READWRITE watchpoint fires on write");
|
||||
printf("PASS fix: READWRITE watchpoint fires on both read and write\n");
|
||||
}
|
||||
|
||||
static void test_fix_remove_at_cleans_sets() {
|
||||
FixedMemory f;
|
||||
f.add(Watchpoint(0x5000, READ));
|
||||
f.add(Watchpoint(0x6000, WRITE));
|
||||
|
||||
f.remove_at(0); // remove 0x5000/READ
|
||||
|
||||
assert(!f.read_mem(0x5000) && "fix: removed watchpoint no longer fires on read");
|
||||
assert(f.write_mem(0x6000) && "fix: remaining watchpoint still fires");
|
||||
printf("PASS fix: remove_at cleans sets correctly\n");
|
||||
}
|
||||
|
||||
static void test_fix_remove_shared_address_preserved() {
|
||||
// Two watchpoints at same address, different types. Remove one, other survives.
|
||||
FixedMemory f;
|
||||
f.add(Watchpoint(0x7000, READ));
|
||||
f.add(Watchpoint(0x7000, WRITE));
|
||||
|
||||
f.remove_at(0); // remove READ watchpoint; WRITE should remain
|
||||
|
||||
assert(!f.read_mem(0x7000) && "fix: READ watchpoint removed, no read hit");
|
||||
assert(f.write_mem(0x7000) && "fix: WRITE watchpoint survives after sibling removed");
|
||||
printf("PASS fix: shared-address partial remove keeps other type intact\n");
|
||||
}
|
||||
|
||||
static void test_speedup_ratio() {
|
||||
const int W = 500;
|
||||
DefectMemory d;
|
||||
FixedMemory f;
|
||||
for (int i = 0; i < W; ++i) {
|
||||
word addr = word(0xC000 + i);
|
||||
d.add(Watchpoint(addr, READWRITE));
|
||||
f.add(Watchpoint(addr, READWRITE));
|
||||
}
|
||||
|
||||
d.read_mem(0x0000);
|
||||
f.read_mem(0x0000);
|
||||
|
||||
printf("PASS speedup: defect=%d scans/read, fix=%d scans/read, ratio ~%dx\n",
|
||||
d.last_read_scanned, f.last_read_scanned, d.last_read_scanned);
|
||||
assert(d.last_read_scanned == W);
|
||||
assert(f.last_read_scanned == 0);
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("--- caprice32-0002 CWE-407 watchpoint O(W) scan per memory access ---\n");
|
||||
test_defect_scans_all_on_miss();
|
||||
test_fix_constant_work_on_miss();
|
||||
test_fix_detects_read_watchpoint();
|
||||
test_fix_readwrite_watchpoint();
|
||||
test_fix_remove_at_cleans_sets();
|
||||
test_fix_remove_shared_address_preserved();
|
||||
test_speedup_ratio();
|
||||
printf("ALL PASS\n");
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue