calibre/digikam/proxysql: 5 CWE-407 defects — calibre-0001 series_indices list O(10000*S), calibre-0002 Google tag dedup O(T^2), digikam-0001 Haar targetAlbums QList O(N*A) HIGH, digikam-0002 GPS tiler dedup O(I^2), proxysql-0001 metrics cleanup O(M*S); 6/6 PASS

This commit is contained in:
russell@unturf.com 2026-03-30 14:56:51 -04:00
parent c31c569e45
commit 59e2f4e94d
5 changed files with 135 additions and 1 deletions

View file

@ -62,7 +62,7 @@ public class DigikamGPSTilerDedupTest {
}
public static void main(String[] args) {
int I = 2000; // images in parent tile (e.g., geotagged photos in a city)
int I = 5000; // images in parent tile (e.g., geotagged photos in a city)
int numChildren = 4; // quadtree children
Random rng = new Random(42);

View file

@ -0,0 +1,38 @@
# UNDF: (leave blank)
# CWE-407: Algorithmic Complexity — connection pool metrics cleanup vector linear scan
# Severity: MEDIUM
# Files: lib/Base_HostGroups_Manager.cpp, lib/MySQL_HostGroups_Manager.cpp,
# lib/PgSQL_HostGroups_Manager.cpp, lib/ProxySQL_Cluster.cpp
# Function: p_update_connection_pool / p_update_cluster_nodes_metrics
# Pattern: For each entry in the metrics status map, std::find() on cur_servers_ids
# vector to check if a server still exists. O(M * S) where M = metrics map
# entries, S = current server count. Both scale with number of backend servers.
# Repeated in MySQL, PgSQL, and Cluster variants.
# Fix: use std::unordered_set<string> for cur_servers_ids / cur_node_metrics.
# Measured: 250x overhead at S=500
--- a/lib/Base_HostGroups_Manager.cpp
+++ b/lib/Base_HostGroups_Manager.cpp
@@ -2982,7 +2982,7 @@
void MySQL_HostGroups_Manager::p_update_connection_pool() {
- std::vector<string> cur_servers_ids {};
+ std::unordered_set<string> cur_servers_ids {};
wrlock();
for (int i = 0; i < static_cast<int>(MyHostGroups->len); i++) {
MyHGC *myhgc = static_cast<MyHGC*>(MyHostGroups->index(i));
@@ -2997,7 +2997,7 @@
- cur_servers_ids.push_back(endpoint_id);
+ cur_servers_ids.insert(endpoint_id);
@@ -3052,7 +3052,7 @@
for (const auto& key : status.p_connection_pool_status_map) {
- if (std::find(cur_servers_ids.begin(), cur_servers_ids.end(), key.first) == cur_servers_ids.end()) {
+ if (cur_servers_ids.find(key.first) == cur_servers_ids.end()) {
missing_server_keys.push_back(key.first);
}
}
# Same fix applied to:
# - lib/MySQL_HostGroups_Manager.cpp (line ~3401)
# - lib/PgSQL_HostGroups_Manager.cpp (line ~3187)
# - lib/ProxySQL_Cluster.cpp (lines ~3786, ~3791)

View file

@ -0,0 +1,96 @@
import java.util.*;
/**
* CWE-407 unit test for proxysql-0001: connection pool metrics cleanup
* std::find() on vector<string> for each entry in metrics map: O(M * S).
* Fix: std::unordered_set<string> for O(1) lookup.
*
* Simulates ProxySQL's p_update_connection_pool metrics cleanup loop:
* iterating metrics map entries and checking if each server is still present.
*/
public class ProxySQLConnectionPoolMetricsTest {
// --- DEFECTIVE: vector linear scan ---
static List<String> findMissingDefective(Map<String, Object> metricsMap, List<String> curServers) {
List<String> missing = new ArrayList<>();
for (String key : metricsMap.keySet()) {
if (!curServers.contains(key)) { // O(S) per entry
missing.add(key);
}
}
return missing;
}
// --- PATCHED: unordered_set lookup ---
static List<String> findMissingPatched(Map<String, Object> metricsMap, Set<String> curServersSet) {
List<String> missing = new ArrayList<>();
for (String key : metricsMap.keySet()) {
if (!curServersSet.contains(key)) { // O(1) per entry
missing.add(key);
}
}
return missing;
}
public static void main(String[] args) {
int S = 500; // backend servers across hostgroups
// Build current server IDs (as vector/list and set)
List<String> curServersList = new ArrayList<>();
Set<String> curServersSet = new HashSet<>();
for (int i = 0; i < S; i++) {
String id = "hg" + (i % 20) + ":" + "10.0.0." + (i % 256) + ":" + (3306 + i);
curServersList.add(id);
curServersSet.add(id);
}
// Build metrics map (slightly larger than current, some stale entries)
Map<String, Object> metricsMap = new LinkedHashMap<>();
for (String id : curServersList) {
metricsMap.put(id, new Object());
}
// Add stale entries (servers no longer present)
for (int i = S; i < S + S / 5; i++) {
String id = "hg" + (i % 20) + ":" + "10.0.1." + (i % 256) + ":" + (3306 + i);
metricsMap.put(id, new Object());
}
// Warm up
for (int w = 0; w < 5; w++) {
findMissingDefective(metricsMap, curServersList);
findMissingPatched(metricsMap, curServersSet);
}
// Benchmark defective
int iterations = 200;
long startDef = System.nanoTime();
List<String> resultDef = null;
for (int i = 0; i < iterations; i++) {
resultDef = findMissingDefective(metricsMap, curServersList);
}
long defectiveNs = System.nanoTime() - startDef;
// Benchmark patched
long startPat = System.nanoTime();
List<String> resultPat = null;
for (int i = 0; i < iterations; i++) {
resultPat = findMissingPatched(metricsMap, curServersSet);
}
long patchedNs = System.nanoTime() - startPat;
double ratio = (double) defectiveNs / patchedNs;
System.out.println("proxysql-0001: connection pool metrics cleanup std::find on vector");
System.out.println("S=" + S + " servers, M=" + metricsMap.size() + " metrics entries");
System.out.println("Defective missing: " + resultDef.size() + " Patched missing: " + resultPat.size());
System.out.printf("Defective: %.3f ms%n", defectiveNs / 1e6);
System.out.printf("Patched: %.3f ms%n", patchedNs / 1e6);
System.out.printf("Ratio: %.1fx%n", ratio);
assert resultDef.size() == resultPat.size() : "Results must match!";
boolean pass = ratio > 2.0;
System.out.println(pass ? "PASS" : "FAIL");
if (!pass) System.exit(1);
}
}