88 lines
3.6 KiB
Diff
88 lines
3.6 KiB
Diff
# UNDF: UNDF-2026-000001135
|
|
# UNDF: UNDF-2026-XXXXXXXXX
|
|
--- a/src/cpu/MSXCPUInterface.hh
|
|
+++ b/src/cpu/MSXCPUInterface.hh
|
|
@@ -260,6 +260,11 @@ public:
|
|
using BreakPoints = std::vector<BreakPoint>;
|
|
[[nodiscard]] static BreakPoints& getBreakPoints() { return breakPoints; }
|
|
|
|
+ // CWE-407 fix: address index for O(1) breakpoint lookup per PC.
|
|
+ // Maps fixed PC address -> list of BreakPoint IDs at that address.
|
|
+ // Dynamic breakpoints (address == nullopt) are not indexed here.
|
|
+ using BreakIndex = std::unordered_map<uint16_t, std::vector<unsigned>>;
|
|
+
|
|
void setCondition(DebugCondition cond);
|
|
void removeCondition(const DebugCondition& cond);
|
|
void removeCondition(unsigned id);
|
|
@@ -460,6 +465,7 @@ private:
|
|
static inline BreakPoints breakPoints; // unsorted
|
|
+ static inline BreakIndex breakIndex; // CWE-407: PC -> [bp-id,...] index
|
|
WatchPoints watchPoints;
|
|
static inline Conditions conditions;
|
|
|
|
--- a/src/cpu/MSXCPUInterface.cc
|
|
+++ b/src/cpu/MSXCPUInterface.cc
|
|
@@ -869,6 +869,10 @@ void MSXCPUInterface::insertBreakPoint(BreakPoint bp)
|
|
{
|
|
cliComm.update(CliComm::UpdateType::DEBUG_UPDT, bp.getIdStr(), "add");
|
|
+ // CWE-407: maintain address index for O(1) per-PC lookup.
|
|
+ if (auto addr = bp.getAddress()) {
|
|
+ breakIndex[*addr].push_back(bp.getId());
|
|
+ }
|
|
breakPoints.push_back(std::move(bp));
|
|
}
|
|
|
|
@@ -876,6 +880,14 @@ void MSXCPUInterface::removeBreakPoint(const BreakPoint& bp)
|
|
{
|
|
cliComm.update(CliComm::UpdateType::DEBUG_UPDT, bp.getIdStr(), "remove");
|
|
+ // CWE-407: remove from address index.
|
|
+ if (auto addr = bp.getAddress()) {
|
|
+ auto& ids = breakIndex[*addr];
|
|
+ ids.erase(std::ranges::find(ids, bp.getId()));
|
|
+ if (ids.empty()) breakIndex.erase(*addr);
|
|
+ }
|
|
breakPoints.erase(find_unguarded(breakPoints, &bp, [](const BreakPoint& i) { return &i; }));
|
|
}
|
|
|
|
@@ -883,7 +895,7 @@ void MSXCPUInterface::removeBreakPoint(unsigned id)
|
|
{
|
|
if (auto it = std::ranges::find(breakPoints, id, &BreakPoint::getId);
|
|
it != breakPoints.end()) {
|
|
cliComm.update(CliComm::UpdateType::DEBUG_UPDT, it->getIdStr(), "remove");
|
|
+ if (auto addr = it->getAddress()) {
|
|
+ auto& ids = breakIndex[*addr];
|
|
+ ids.erase(std::ranges::find(ids, id));
|
|
+ if (ids.empty()) breakIndex.erase(*addr);
|
|
+ }
|
|
breakPoints.erase(it);
|
|
}
|
|
}
|
|
@@ -892,12 +906,30 @@ bool MSXCPUInterface::checkBreakPoints(unsigned pc)
|
|
{
|
|
// create copy for the case that breakpoint/condition removes itself
|
|
// - avoids iterating over a changing collection
|
|
- // CWE-407 DEFECT: O(B) scan over ALL breakpoints per Z80 instruction.
|
|
- // With B breakpoints active, every instruction pays the full scan cost
|
|
- // regardless of PC value. Fix: use breakIndex for O(k) lookup where
|
|
- // k = breakpoints at this exact address (almost always 0 or 1).
|
|
std::vector<BreakPoint> bpCopy;
|
|
- for (const auto& bp : breakPoints) {
|
|
- if (bp.isEnabled() && bp.getAddress() == pc) bpCopy.push_back(bp);
|
|
+ // CWE-407 FIX: O(k) lookup via address index instead of O(B) full scan.
|
|
+ // k = breakpoints set at exactly this PC, usually 0.
|
|
+ if (auto it = breakIndex.find(uint16_t(pc)); it != breakIndex.end()) {
|
|
+ for (unsigned bpId : it->second) {
|
|
+ if (auto bpIt = std::ranges::find(breakPoints, bpId, &BreakPoint::getId);
|
|
+ bpIt != breakPoints.end() && bpIt->isEnabled()) {
|
|
+ bpCopy.push_back(*bpIt);
|
|
+ }
|
|
+ }
|
|
}
|
|
+ // Dynamic breakpoints (address == nullopt) still need full scan, but
|
|
+ // these are uncommon; typical usage has zero dynamic breakpoints.
|
|
+ for (const auto& bp : breakPoints) {
|
|
+ if (bp.isEnabled() && !bp.getAddress()) bpCopy.push_back(bp);
|
|
+ }
|
|
std::vector<DebugCondition> condCopy;
|
|
for (const auto& cond : conditions) {
|
|
if (cond.isEnabled()) condCopy.push_back(cond);
|