java-topology/defects/openmsx-0001/patch/openmsx-0001.patch

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);