java-topology/defects/wesnoth-0001/patch/wesnoth-0001.patch
russell@unturf.com 9fac7766ba wesnoth: 3 CWE-407 defects, MOAD 0002-0005 CLEAN
wesnoth-0001: A* pathfinding std::find on pq vector for decrease-key
  O(V*Q) per relaxation, fix: lazy deletion. HIGH, 1279x at N=5000.
wesnoth-0002: server ip_log_ deque linear scan on login/logoff
  O(N) per event with N up to 500. MEDIUM, 437x at L=2000.
wesnoth-0003: combine_special_notes O(N^2) vector dedup
  utils::contains on vector per note insertion. MEDIUM, 499x at N=1000.

MOAD-0002 (Intertangle): singletons deeply embedded, not actionable.
MOAD-0003 (Leaked Context): thread_local for debug/call-stack only.
MOAD-0004 (Logged Secret): passwords never logged verbatim.
MOAD-0005 (Thundering Herd): single-threaded game + coroutine server.

6/6 unit tests PASS.
2026-03-31 12:14:20 -04:00

46 lines
1.6 KiB
Diff

# UNDF: UNDF-2026-000000970
--- a/src/pathfind/astarsearch.cpp
+++ b/src/pathfind/astarsearch.cpp
@@ -172,9 +172,16 @@ plain_route a_star_search(const map_location& src, const map_location& dst,
std::vector<int> pq;
pq.push_back(index(src));
+ // Lazy-deletion A*: when a node is relaxed while already in the open
+ // set, we push a duplicate entry instead of performing a decrease-key.
+ // Stale duplicates are discarded when popped (the node will already
+ // have in == search_counter, meaning it was finalized).
while (!pq.empty()) {
- node& n = nodes[pq.front()];
-
- n.in = search_counter;
std::pop_heap(pq.begin(), pq.end(), node_comp);
- pq.pop_back();
+ int front_idx = pq.back();
+ pq.pop_back();
+
+ node& n = nodes[front_idx];
+
+ // Skip stale duplicates from lazy decrease-key.
+ if (n.in == search_counter) continue;
+ n.in = search_counter;
if (n.t >= dst_node.g) break;
@@ -213,10 +220,10 @@ plain_route a_star_search(const map_location& src, const map_location& dst,
next = node(cost, loc, n.curr, dst, true, teleports, srch, dsth);
- if (in_list) {
- std::push_heap(pq.begin(), std::find(pq.begin(), pq.end(), static_cast<int>(index(loc))) + 1, node_comp);
- } else {
- pq.push_back(index(loc));
- std::push_heap(pq.begin(), pq.end(), node_comp);
- }
+ // Always push a new entry. If the node was already in the
+ // open set (in_list == true), the old entry becomes stale
+ // and will be skipped when popped (lazy deletion).
+ pq.push_back(index(loc));
+ std::push_heap(pq.begin(), pq.end(), node_comp);
}
}