wave17: postgres-0001/asterisk-0001/haproxy-0001/nginx-0001; postfix+bevy CLEAN

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)
This commit is contained in:
russell@unturf.com 2026-03-30 07:57:30 -04:00
parent da8b4a41d6
commit ff6292d067
10 changed files with 627 additions and 252 deletions

View file

@ -0,0 +1,108 @@
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -2871,38 +2871,50 @@ ngx_http_link_multi_headers(ngx_http_request_t *r)
{
- ngx_uint_t i, j;
- ngx_list_part_t *part, *ppart;
- ngx_table_elt_t *header, *pheader, **ph;
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *header, **ph;
+ ngx_hash_t name_map;
+ ngx_hash_init_t hash;
+ ngx_hash_key_t *keys;
+ ngx_uint_t nkeys;
if (r->headers_in.multi_linked) {
return NGX_OK;
}
r->headers_in.multi_linked = 1;
part = &r->headers_in.headers.part;
header = part->elts;
- for (i = 0; /* void */; i++) {
-
- if (i >= part->nelts) {
- if (part->next == NULL) {
- break;
- }
-
- part = part->next;
- header = part->elts;
- i = 0;
- }
-
- header[i].next = NULL;
-
- ppart = &r->headers_in.headers.part;
- pheader = ppart->elts;
-
- for (j = 0; /* void */; j++) {
-
- if (j >= ppart->nelts) {
- if (ppart->next == NULL) {
- break;
- }
-
- ppart = ppart->next;
- pheader = ppart->elts;
- j = 0;
- }
-
- if (part == ppart && i == j) {
- break;
- }
-
- if (header[i].key.len == pheader[j].key.len
- && ngx_strncasecmp(header[i].key.data, pheader[j].key.data,
- header[i].key.len)
- == 0)
- {
- ph = &pheader[j].next;
- while (*ph) { ph = &(*ph)->next; }
- *ph = &header[i];
-
- r->headers_in.multi = 1;
-
- break;
- }
- }
- }
+ /*
+ * Build a hash table mapping lowercased header name → first ngx_table_elt_t *.
+ * Single O(H) forward pass: for each header, if the name is already in the
+ * hash, append to that entry's ->next chain. No backward re-scan needed.
+ * Replaces the O(H²) double-traversal that compared every (i,j) pair.
+ */
+ ngx_hash_keys_array_t ha;
+ if (ngx_hash_keys_array_init(&ha, NGX_HASH_SMALL, r->pool) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ for (i = 0; /* void */; i++) {
+ if (i >= part->nelts) {
+ if (part->next == NULL) break;
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ header[i].next = NULL;
+
+ ngx_table_elt_t **existing = ngx_hash_find(&name_map,
+ header[i].lowcase_key ?
+ ngx_hash_key(header[i].lowcase_key, header[i].key.len) :
+ ngx_hash_key_lc(header[i].key.data, header[i].key.len),
+ header[i].key.data,
+ header[i].key.len);
+ if (existing) {
+ ph = &(*existing)->next;
+ while (*ph) { ph = &(*ph)->next; }
+ *ph = &header[i];
+ r->headers_in.multi = 1;
+ } else {
+ ngx_hash_add_key(&ha, &header[i].key, &header[i], NGX_HASH_READONLY_KEY);
+ }
+ }

View file

@ -0,0 +1,87 @@
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");
}
}