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)
90 lines
3.7 KiB
Java
90 lines
3.7 KiB
Java
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 100–10000 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");
|
||
}
|
||
}
|