java-topology/defects/nginx/unit/NginxKeepaliveCacheLinearScanTest.java
russell@unturf.com ba818693db nmap-0002 + haproxy-0004 + nginx-0004 + weechat-0003 + zeek-0002 + curl-0004: 6 new CWE-407 defects in network tools; count 693→699
nmap-0002:     nmap.cc merge_port_lists O(N²) port dedup → unordered_set O(N); ~65000x at max range
haproxy-0004:  http_ana.c http_capture_headers O(H×C) cap_hdr walk per request → pre-built HashMap O(H)
nginx-0004:    ngx_http_upstream_keepalive_module.c keepalive_get_peer O(C) sockaddr scan per upstream request → HashMap O(1)
weechat-0003:  irc-channel.c irc_channel_search O(C) linked-list scan per message handler → channels_hashtable O(1)
zeek-0002:     Attr.cc Attributes::AddAttrs O(A²) triple-Find/RemoveAttr per attr → unordered_map index O(A)
curl-0004:     mime.c search_header O(P×H) 3x per part per mime_add_headers → pre-indexed header name set O(P)
2026-03-29 22:22:11 -04:00

90 lines
3.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.*;
/**
* nginx-0004 — CWE-407: ngx_http_upstream_keepalive_get_peer O(C) per upstream request
*
* Models src/http/modules/ngx_http_upstream_keepalive_module.c
* ngx_http_upstream_keepalive_get_peer() (~line 212):
* Slow: iterates free[] queue scanning for matching sockaddr → O(C) per request
* where C = keepalive cache size (default 0, but configured 10010000 in production)
* Fast: HashMap<sockaddrKey, connection> → O(1) per request
*
* Hot path: called on every upstream HTTP request that could reuse a keepalive connection.
* With `keepalive 1000` configured and many distinct upstreams, degrades to O(1000) per request.
*/
public class NginxKeepaliveCacheLinearScanTest {
// Simulates ngx_connection_t / ngx_peer_connection_t with a sockaddr key
static class KeepaliveConn {
final String sockaddrKey; // e.g., "192.168.1.1:8080"
KeepaliveConn(String sockaddrKey) { this.sockaddrKey = sockaddrKey; }
}
// --- SLOW: O(C) linear scan (defect) ---
// Models: for (q = ngx_queue_head(cache); q != ngx_queue_sentinel(cache); q = ngx_queue_next(q))
// item = ngx_queue_data(q, ...); if (ngx_memn2cmp(sockaddr, item->sockaddr) == 0) found
static long keepaliveGetPeerSlow(List<KeepaliveConn> cache, String target) {
long ops = 0;
for (KeepaliveConn conn : cache) {
ops++;
if (conn.sockaddrKey.equals(target)) break;
}
return ops;
}
// --- FAST: O(1) hash lookup (fix) ---
// Models: pre-built HashMap keyed by sockaddr at connection cache time
static long keepaliveGetPeerFast(Map<String, KeepaliveConn> index, String target) {
index.get(target);
return 1;
}
// Build a keepalive cache with C connections to distinct upstream addresses
static List<KeepaliveConn> buildCache(int C) {
List<KeepaliveConn> cache = new ArrayList<>(C);
for (int i = 0; i < C; i++)
cache.add(new KeepaliveConn("10.0." + (i / 256) + "." + (i % 256) + ":8080"));
return cache;
}
static Map<String, KeepaliveConn> buildIndex(List<KeepaliveConn> cache) {
Map<String, KeepaliveConn> map = new HashMap<>(cache.size() * 2);
for (KeepaliveConn c : cache) map.put(c.sockaddrKey, c);
return map;
}
public static void main(String[] args) {
System.out.println("nginx-0004 CWE-407: keepalive_get_peer O(C) vs O(1)");
System.out.println("======================================================");
// R = number of upstream requests, C = keepalive cache size
// target is the last entry (worst-case scan)
int[][] params = { {100, 100}, {500, 500}, {1000, 1000} };
for (int[] p : params) {
int R = p[0], C = p[1];
List<KeepaliveConn> cache = buildCache(C);
Map<String, KeepaliveConn> index = buildIndex(cache);
// Worst-case: target is the last connection in the cache
String target = cache.get(C - 1).sockaddrKey;
long slowTotal = 0, fastTotal = 0;
for (int r = 0; r < R; r++) {
slowTotal += keepaliveGetPeerSlow(cache, target);
fastTotal += keepaliveGetPeerFast(index, target);
}
double ratio = (double) slowTotal / fastTotal;
System.out.printf(" C=%4d cache, R=%4d requests (worst-case): slow=%,8d ops fast=%,5d ops speedup=%.0fx%n",
C, R, slowTotal, fastTotal, ratio);
assert ratio >= (double) C / 2 :
"Expected speedup >= " + (C/2) + "x but got " + ratio + " (C=" + C + ")";
}
System.out.println("\nPASS");
}
}