java-topology/defects/wesnoth-0002/patch/wesnoth-0002.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

63 lines
2 KiB
Diff

# UNDF: UNDF-2026-000000971
--- a/src/server/wesnothd/server.hpp
+++ b/src/server/wesnothd/server.hpp
@@ -111,6 +111,18 @@ private:
struct connection_log
{
std::string nick, ip;
+ std::chrono::system_clock::time_point log_off;
+
+ bool operator==(const connection_log& c) const
+ {
+ // log off time does not matter to find ip-nick pairs
+ return c.nick == nick && c.ip == ip;
+ }
+ };
+
+ struct connection_log_hash
+ {
+ std::size_t operator()(const connection_log& c) const
{
+ std::size_t h1 = std::hash<std::string>{}(c.nick);
+ std::size_t h2 = std::hash<std::string>{}(c.ip);
+ return h1 ^ (h2 << 1);
}
};
- std::deque<connection_log> ip_log_;
+ // Use an unordered_set for O(1) lookup instead of linear scan on deque.
+ // Maintain a deque alongside for LRU eviction order.
+ std::deque<connection_log> ip_log_;
+ std::unordered_set<connection_log, connection_log_hash> ip_log_set_;
--- a/src/server/wesnothd/server.cpp
+++ b/src/server/wesnothd/server.cpp
@@ -896,7 +896,7 @@ void server::handle_new_client(socket_ptr socket)
connection_log ip_name { username, client_address(socket), {} };
- if(std::find(ip_log_.begin(), ip_log_.end(), ip_name) == ip_log_.end()) {
+ if(ip_log_set_.find(ip_name) == ip_log_set_.end()) {
ip_log_.push_back(ip_name);
+ ip_log_set_.insert(ip_name);
// Remove the oldest entry if the size of the IP log exceeds the maximum size
if(ip_log_.size() > max_ip_log_size_) {
+ ip_log_set_.erase(ip_log_.front());
ip_log_.pop_front();
}
}
@@ -2279,7 +2279,7 @@ void server::remove_player(player_iterator iter)
connection_log ip_name { iter->info().name(), ip, {} };
- auto i = std::find(ip_log_.begin(), ip_log_.end(), ip_name);
+ auto i = ip_log_set_.find(ip_name);
- if(i != ip_log_.end()) {
+ if(i != ip_log_set_.end()) {
- i->log_off = std::chrono::system_clock::now();
+ // Update log_off in the deque entry
+ auto di = std::find(ip_log_.begin(), ip_log_.end(), ip_name);
+ if(di != ip_log_.end()) {
+ di->log_off = std::chrono::system_clock::now();
+ }
}