postgres-0001: pg_inherits.c typeInheritsFrom() BFS visited List → HTAB O(1) asterisk-0001: app_queue.c interface_exists() ao2_iterator walk → ao2_find O(1) haproxy-0001: http_ana.c cookie-server scan linked-list → eb-tree index O(log S) nginx-0001: ngx_http_link_multi_headers() O(H²) double-scan → O(H) hash pass postfix: CLEAN (htable throughout; two marginal LOW admin-bounded candidates) bevy: CLEAN (FixedBitSet/HashSet throughout; only hardware-bounded marginals)
87 lines
3.5 KiB
Java
87 lines
3.5 KiB
Java
import java.util.*;
|
||
|
||
/**
|
||
* CWE-407 unit test for nginx ngx_http_core_module.c defect.
|
||
*
|
||
* nginx-0001: src/http/ngx_http_core_module.c ngx_http_link_multi_headers()
|
||
* Nested double-traversal of r->headers_in.headers to chain duplicate
|
||
* header names: outer loop at position i, inner loop scans 0..i-1.
|
||
* O(H²) where H = number of incoming request headers.
|
||
* Fix: single O(H) pass using a hash map of name → first header pointer;
|
||
* append to chain on first match, no backward scan needed.
|
||
*/
|
||
public class NginxTest {
|
||
|
||
// Simulate defect: O(H²) double-traversal
|
||
static Map<String, List<Integer>> linkMultiHeaders_quadratic(String[] headers) {
|
||
// Returns map of name -> list of positions (the "chain")
|
||
Map<String, List<Integer>> chains = new LinkedHashMap<>();
|
||
for (int i = 0; i < headers.length; i++) {
|
||
boolean linked = false;
|
||
for (int j = 0; j < i; j++) { // O(H²) total — defect
|
||
if (headers[j].equalsIgnoreCase(headers[i])) {
|
||
chains.get(headers[j].toLowerCase()).add(i);
|
||
linked = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!linked) {
|
||
List<Integer> chain = new ArrayList<>();
|
||
chain.add(i);
|
||
chains.put(headers[i].toLowerCase(), chain);
|
||
}
|
||
}
|
||
return chains;
|
||
}
|
||
|
||
// Simulate fix: O(H) single pass with hash map
|
||
static Map<String, List<Integer>> linkMultiHeaders_hash(String[] headers) {
|
||
Map<String, List<Integer>> chains = new LinkedHashMap<>();
|
||
for (int i = 0; i < headers.length; i++) {
|
||
String lower = headers[i].toLowerCase();
|
||
chains.computeIfAbsent(lower, k -> new ArrayList<>()).add(i); // O(1)
|
||
}
|
||
return chains;
|
||
}
|
||
|
||
static void testNginx0001() throws Exception {
|
||
// Simulate H headers with many duplicates (worst case for O(H²))
|
||
// nginx default: ~50 unique header names, many repeated → H=300 total
|
||
int H = 300;
|
||
int UNIQUE = 20;
|
||
String[] headers = new String[H];
|
||
String[] names = new String[UNIQUE];
|
||
for (int i = 0; i < UNIQUE; i++) names[i] = "X-Header-" + i;
|
||
for (int i = 0; i < H; i++) headers[i] = names[i % UNIQUE];
|
||
|
||
// correctness
|
||
Map<String, List<Integer>> r1 = linkMultiHeaders_quadratic(headers);
|
||
Map<String, List<Integer>> r2 = linkMultiHeaders_hash(headers);
|
||
assert r1.keySet().equals(r2.keySet()) : "must produce same key set";
|
||
for (String k : r1.keySet()) {
|
||
assert r1.get(k).equals(r2.get(k)) :
|
||
"chain mismatch for " + k + ": " + r1.get(k) + " vs " + r2.get(k);
|
||
}
|
||
|
||
// performance
|
||
int REPS = 10_000;
|
||
long t0 = System.nanoTime();
|
||
for (int r = 0; r < REPS; r++) linkMultiHeaders_quadratic(headers);
|
||
long tQuad = System.nanoTime() - t0;
|
||
|
||
t0 = System.nanoTime();
|
||
for (int r = 0; r < REPS; r++) linkMultiHeaders_hash(headers);
|
||
long tHash = System.nanoTime() - t0;
|
||
|
||
double ratio = (double) tQuad / tHash;
|
||
System.out.printf("nginx-0001: quadratic=%.3fs hash=%.3fs ratio=%.1f×%n",
|
||
tQuad / 1e9, tHash / 1e9, ratio);
|
||
assert ratio > 3 : "Expected >3× speedup, got " + ratio;
|
||
System.out.println("PASS nginx-0001");
|
||
}
|
||
|
||
public static void main(String[] args) throws Exception {
|
||
testNginx0001();
|
||
System.out.println("ALL PASS");
|
||
}
|
||
}
|