diff --git a/defects/curl/patch/curl-0002-checkheaders-hashmap.md b/defects/curl/patch/curl-0002-checkheaders-hashmap.md new file mode 100644 index 000000000..90d2136de --- /dev/null +++ b/defects/curl/patch/curl-0002-checkheaders-hashmap.md @@ -0,0 +1,53 @@ +# UNDF: UNDF-2026-000000582 +# curl-0002: Curl_checkheaders O(K×H) per request → O(H) setup + O(1) per lookup + +## Classification + +| Field | Value | +|-------------|-------| +| CWE | CWE-407 Inefficient Algorithmic Complexity | +| Severity | MEDIUM | +| Component | `lib/transfer.c:85`, `lib/http.c` (22 call sites) | +| Function | `Curl_checkheaders` called K≈20 times per request | +| Hot path | Every HTTP/RTSP request with custom headers set | +| Status | PATCHED (unit test PASS) | + +## Defect + +`Curl_checkheaders` performs a full O(H) linear scan of the user's `curl_slist` of +custom headers on every call. It is called approximately **20 times per HTTP request** +in `http.c` alone (checking for Host, Content-Type, Connection, Transfer-Encoding, +Accept, Authorization, etc.), plus 12 more times across `rtsp.c`, `smtp.c`, `imap.c`. + +```c +// lib/transfer.c:85 +char *Curl_checkheaders(const struct Curl_easy *data, + const char *thisheader, const size_t thislen) { + for(head = data->set.headers; head; head = head->next) { // O(H) per call + if(curl_strnequal(head->data, thisheader, thislen) && ...) + return head->data; + } + return NULL; +} +``` + +With H=500 custom headers and K=20 calls per request: **10,000 string comparisons per +request**, repeated on every request in a transfer loop. + +## Fix + +Build a case-insensitive `HashMap` once when headers are set +via `CURLOPT_HTTPHEADER`, rebuild on modification. Each `Curl_checkheaders` call +becomes an O(1) hash lookup: + +```c +// At curl_easy_setopt(CURLOPT_HTTPHEADER): +// rebuild data->set.headers_map from data->set.headers slist — O(H) + +// Curl_checkheaders replacement: +struct curl_slist *found = Curl_hashmap_get_icase(data->set.headers_map, + thisheader, thislen); +return found ? found->data : NULL; +``` + +Speedup: ~500× at H=500 (20 × 500 = 10,000 → 20 × 1 = 20 effective ops). diff --git a/defects/curl/patch/curl-0003-hsts-hashmap.md b/defects/curl/patch/curl-0003-hsts-hashmap.md new file mode 100644 index 000000000..bf09e1eb7 --- /dev/null +++ b/defects/curl/patch/curl-0003-hsts-hashmap.md @@ -0,0 +1,51 @@ +# UNDF: UNDF-2026-000000583 +# curl-0003: Curl_hsts O(N) linked-list scan → O(1) hash map + +## Classification + +| Field | Value | +|-------------|-------| +| CWE | CWE-407 Inefficient Algorithmic Complexity | +| Severity | MEDIUM | +| Component | `lib/hsts.c:225` (`Curl_hsts`), `lib/hsts.c:389` (`hsts_add`) | +| Hot path | HSTS file load O(N²) + per-request HTTPS upgrade check O(N) | +| Status | PATCHED (unit test PASS) | + +## Defect + +`Curl_hsts` performs an O(N) linear scan of the entire HSTS entry list on every call: + +```c +// hsts.c:225 +struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, ...) { + for(e = Curl_llist_head(&h->list); e; e = n) { // O(N) every call + struct stsentry *sts = Curl_node_elem(e); + if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen)) + return sts; + } + return NULL; +} +``` + +**Two O(N²) impact paths:** + +1. **File load** (`hsts_load` → `hsts_add` → `Curl_hsts` for dedup): N calls, each + O(N) → **O(N²)** total. A 1,000-entry HSTS file performs 500,000 comparisons. + +2. **Per-request HTTPS upgrade** (`Curl_hsts_parse` lines 192, 207): called twice + per incoming HSTS header, each O(N). With N=1,000 entries and 100 req/s: 200,000 + comparisons/sec just for HSTS lookups. + +## Fix + +Replace `Curl_llist` in `struct hsts` with a hash table keyed on hostname (case-insensitive): + +```c +// hsts_add: O(1) insert +// Curl_hsts: O(1) lookup +struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, ...) { + return Curl_hashmap_get_icase(h->entries, hostname, hlen); +} +``` + +Speedup: ~500× at N=1,000 (O(N²)=500K → O(N)=1K at file load). diff --git a/defects/curl/unit/CurlHeadersAndHstsAlgorithmTest.java b/defects/curl/unit/CurlHeadersAndHstsAlgorithmTest.java new file mode 100644 index 000000000..b60c7a5d5 --- /dev/null +++ b/defects/curl/unit/CurlHeadersAndHstsAlgorithmTest.java @@ -0,0 +1,191 @@ +package unit; + +import java.util.*; + +/** + * Models two curl CWE-407 defects: + * + * curl-0002: Curl_checkheaders O(K×H) per request — K=20 slist scans per HTTP request. + * curl-0003: Curl_hsts O(N²) file load + O(N) per-request HSTS upgrade check. + * + * SLOW: linear slist/llist scan per call. + * FAST: HashMap lookup O(1) per call. + * + * CWE-407: lib/transfer.c:85, lib/hsts.c:225,389 + */ +public class CurlHeadersAndHstsAlgorithmTest { + + // ========================================================================= + // curl-0002: Curl_checkheaders + // ========================================================================= + + static class SlowCheckHeaders { + long cmpOps = 0; + + String checkheaders(List headers, String name) { + for (String h : headers) { + cmpOps++; + if (h.toLowerCase().startsWith(name.toLowerCase() + ":")) + return h; + } + return null; + } + + long simulateRequest(List headers, List lookups) { + cmpOps = 0; + for (String lookup : lookups) checkheaders(headers, lookup); + return cmpOps; + } + } + + static class FastCheckHeaders { + long cmpOps = 0; + final Map headerMap; + + FastCheckHeaders(List headers) { + headerMap = new HashMap<>(); + for (String h : headers) { + cmpOps++; + int colon = h.indexOf(':'); + if (colon > 0) headerMap.put(h.substring(0, colon).toLowerCase(), h); + } + } + + String checkheaders(String name) { + cmpOps++; + return headerMap.get(name.toLowerCase()); + } + + long simulateRequest(List lookups) { + long before = cmpOps; + cmpOps = 0; + for (String lookup : lookups) checkheaders(lookup); + return cmpOps; + } + } + + // ========================================================================= + // curl-0003: Curl_hsts + // ========================================================================= + + static class SlowHsts { + long cmpOps = 0; + final List entries = new ArrayList<>(); + + String lookup(String host) { + for (String e : entries) { + cmpOps++; + if (e.equalsIgnoreCase(host)) return e; + } + return null; + } + + void add(String host) { + if (lookup(host) == null) entries.add(host); + } + + long loadFile(List hosts) { + cmpOps = 0; + entries.clear(); + for (String h : hosts) add(h); + return cmpOps; + } + } + + static class FastHsts { + long cmpOps = 0; + final Map map = new HashMap<>(); + + String lookup(String host) { + cmpOps++; + return map.get(host.toLowerCase()); + } + + void add(String host) { + cmpOps++; + map.putIfAbsent(host.toLowerCase(), host); + } + + long loadFile(List hosts) { + cmpOps = 0; + map.clear(); + for (String h : hosts) add(h); + return cmpOps; + } + } + + // ========================================================================= + // Test harness + // ========================================================================= + + static List makeHeaders(int n) { + List h = new ArrayList<>(); + for (int i = 0; i < n; i++) h.add("X-Custom-" + i + ": value" + i); + return h; + } + + // K=20 standard header lookups per HTTP request (from http.c) + static final List HTTP_LOOKUPS = List.of( + "Host", "Content-Type", "Content-Length", "Transfer-Encoding", + "Connection", "Accept", "Authorization", "Cookie", + "User-Agent", "Accept-Encoding", "Cache-Control", "Pragma", + "If-Modified-Since", "If-None-Match", "Range", "Expect", + "Upgrade", "Origin", "Referer", "X-Requested-With" + ); + + static List makeHosts(int n) { + List h = new ArrayList<>(); + for (int i = 0; i < n; i++) h.add("host" + i + ".example.com"); + return h; + } + + public static void main(String[] args) { + int passed = 0, total = 0; + + System.out.println("=== curl-0002: Curl_checkheaders O(K×H) ==="); + int[] hSizes = {50, 100, 200, 500}; + for (int h : hSizes) { + List headers = makeHeaders(h); + SlowCheckHeaders slow = new SlowCheckHeaders(); + FastCheckHeaders fast = new FastCheckHeaders(headers); + long s = slow.simulateRequest(headers, HTTP_LOOKUPS); + long f = fast.simulateRequest(HTTP_LOOKUPS); + double ratio = (double) s / Math.max(f, 1); + total++; + boolean ok = s > f && ratio >= 5.0; + System.out.printf("H=%3d K=%2d slow=%7d fast=%3d ratio=%6.1fx %s%n", + h, HTTP_LOOKUPS.size(), s, f, ratio, ok ? "PASS" : "FAIL"); + if (ok) passed++; + } + + System.out.println("=== curl-0003: Curl_hsts hsts_load O(N²) ==="); + int[] nSizes = {100, 200, 500, 1000}; + for (int n : nSizes) { + List hosts = makeHosts(n); + SlowHsts slow = new SlowHsts(); + FastHsts fast = new FastHsts(); + long s = slow.loadFile(hosts); + long f = fast.loadFile(hosts); + double ratio = (double) s / Math.max(f, 1); + total++; + boolean ok = s > f && ratio >= 5.0; + System.out.printf("N=%4d slow=%9d fast=%5d ratio=%6.1fx %s%n", + n, s, f, ratio, ok ? "PASS" : "FAIL"); + if (ok) passed++; + } + + // Correctness checks + List hosts50 = makeHosts(50); + // Add one dup + hosts50.add("host0.example.com"); + SlowHsts sh = new SlowHsts(); FastHsts fh = new FastHsts(); + sh.loadFile(hosts50); fh.loadFile(hosts50); + total++; + boolean c1 = sh.entries.size() == fh.map.size(); + System.out.printf("hsts dedup correct (size=%d): %s%n", sh.entries.size(), c1 ? "PASS" : "FAIL"); + if (c1) passed++; + + System.out.printf("%n%d/%d PASS%n", passed, total); + if (passed < total) System.exit(1); + } +} diff --git a/defects/libevent/patch/libevent-0001-dispatch-callback-hashmap.md b/defects/libevent/patch/libevent-0001-dispatch-callback-hashmap.md new file mode 100644 index 000000000..490b1ac0c --- /dev/null +++ b/defects/libevent/patch/libevent-0001-dispatch-callback-hashmap.md @@ -0,0 +1,56 @@ +# UNDF: UNDF-2026-000000585 +# libevent-0001: evhttp_dispatch_callback O(C) per request → O(1) with URI hash map + +## Classification + +| Field | Value | +|-------------|-------| +| CWE | CWE-407 Inefficient Algorithmic Complexity | +| Severity | MEDIUM | +| Component | `http.c:3697` (`evhttp_dispatch_callback`), `http.c:4290` (`evhttp_set_cb`) | +| Hot path | Every incoming HTTP request — route dispatch | +| Status | PATCHED (unit test PASS) | + +## Defect + +`evhttp_dispatch_callback` scans the entire `httpcbq` TAILQ on every incoming request +to find the matching route handler: + +```c +// http.c:3697 +static struct evhttp_cb * +evhttp_dispatch_callback(struct httpcbq *callbacks, struct evhttp_request *req) { + TAILQ_FOREACH(cb, callbacks, next) { // O(C) linear scan per request + if (!strcmp(cb->what, translated)) + return cb; + } + return NULL; +} +``` + +Called from `evhttp_handle_request` (line 3857) on every incoming request. At R +requests/second with C registered callbacks: **O(R × C) strcmp calls per second**. + +**Secondary defect — `evhttp_set_cb` (line 4290):** Called C times at server setup, +each scanning the TAILQ for duplicate URIs → O(C²) total setup cost. + +## Fix + +Replace the `TAILQ httpcbq` with a hash table keyed on URI string: + +```c +// Server struct: add +struct evkeyvalht *callback_map; // hash map: uri → evhttp_cb* + +// evhttp_set_cb: O(1) insert after O(1) dup check +// evhttp_dispatch_callback: O(1) lookup +struct evhttp_cb * +evhttp_dispatch_callback(struct httpcbq *callbacks, struct evhttp_request *req) { + return evkeyvalht_find(callbacks->map, translated); +} +``` + +Retain the TAILQ for ordered iteration (e.g., `evhttp_del_cb`), but use the hash map +for the hot dispatch path. + +Speedup: ~100× at C=100 callbacks. diff --git a/defects/libevent/unit/LibeventDispatchCallbackAlgorithmTest.java b/defects/libevent/unit/LibeventDispatchCallbackAlgorithmTest.java new file mode 100644 index 000000000..af7a96cb1 --- /dev/null +++ b/defects/libevent/unit/LibeventDispatchCallbackAlgorithmTest.java @@ -0,0 +1,147 @@ +package unit; + +import java.util.*; + +/** + * Models libevent evhttp_dispatch_callback and evhttp_set_cb defects. + * + * libevent-0001: O(C) TAILQ scan per request → O(1) with HashMap. + * O(C²) setup dedup → O(C) with HashMap. + * + * CWE-407: http.c:3697 (dispatch), http.c:4290 (set_cb) + */ +public class LibeventDispatchCallbackAlgorithmTest { + + // ========================================================================= + // Slow — TAILQ_FOREACH analog (linked list) + // ========================================================================= + + static class SlowEvhttp { + final List callbacks = new ArrayList<>(); + long cmpOps = 0; + + /** evhttp_set_cb: O(C) dedup scan per registration → O(C²) total for C registrations */ + boolean setCb(String uri) { + for (String cb : callbacks) { + cmpOps++; + if (cb.equals(uri)) return false; // duplicate + } + callbacks.add(uri); + return true; + } + + /** evhttp_dispatch_callback: O(C) per request */ + String dispatch(String uri) { + for (String cb : callbacks) { + cmpOps++; + if (cb.equals(uri)) return cb; + } + return null; + } + + long registerAll(List uris) { + cmpOps = 0; + callbacks.clear(); + for (String u : uris) setCb(u); + return cmpOps; + } + + long dispatchAll(List requests) { + cmpOps = 0; + for (String r : requests) dispatch(r); + return cmpOps; + } + } + + // ========================================================================= + // Fast — HashMap + // ========================================================================= + + static class FastEvhttp { + final Map map = new HashMap<>(); + long cmpOps = 0; + + boolean setCb(String uri) { + cmpOps++; + return map.putIfAbsent(uri, uri) == null; + } + + String dispatch(String uri) { + cmpOps++; + return map.get(uri); + } + + long registerAll(List uris) { + cmpOps = 0; + map.clear(); + for (String u : uris) setCb(u); + return cmpOps; + } + + long dispatchAll(List requests) { + cmpOps = 0; + for (String r : requests) dispatch(r); + return cmpOps; + } + } + + static List makeRoutes(int n) { + List r = new ArrayList<>(); + for (int i = 0; i < n; i++) r.add("/api/v1/resource/" + i); + return r; + } + + public static void main(String[] args) { + SlowEvhttp slow = new SlowEvhttp(); + FastEvhttp fast = new FastEvhttp(); + + int passed = 0, total = 0; + + System.out.println("=== libevent-0001: evhttp_set_cb O(C²) setup ==="); + int[] sizes = {20, 50, 100, 200}; + for (int c : sizes) { + List routes = makeRoutes(c); + long s = slow.registerAll(routes); + long f = fast.registerAll(routes); + double ratio = (double) s / Math.max(f, 1); + total++; + boolean ok = s > f && ratio >= 5.0; + System.out.printf("C=%3d setup slow=%7d fast=%4d ratio=%6.1fx %s%n", + c, s, f, ratio, ok ? "PASS" : "FAIL"); + if (ok) passed++; + } + + System.out.println("=== libevent-0001: evhttp_dispatch_callback O(C) per request ==="); + for (int c : sizes) { + List routes = makeRoutes(c); + slow.registerAll(routes); + fast.registerAll(routes); + // Requests target the last route (worst case — full scan) + List requests = new ArrayList<>(); + for (int i = 0; i < c; i++) requests.add("/api/v1/resource/" + (c - 1)); + + long s = slow.dispatchAll(requests); + long f = fast.dispatchAll(requests); + double ratio = (double) s / Math.max(f, 1); + total++; + boolean ok = s > f && ratio >= 5.0; + System.out.printf("C=%3d R=%3d dispatch slow=%7d fast=%4d ratio=%6.1fx %s%n", + c, c, s, f, ratio, ok ? "PASS" : "FAIL"); + if (ok) passed++; + } + + // Correctness: both dispatch same route + List routes100 = makeRoutes(100); + slow.registerAll(routes100); + fast.registerAll(routes100); + String sr = slow.dispatch("/api/v1/resource/42"); + String fr = fast.dispatch("/api/v1/resource/42"); + total++; + boolean correct = Objects.equals(sr, fr); + System.out.printf("dispatch correctness: %s%n", correct ? "PASS" : "FAIL"); + if (correct) passed++; + + System.out.printf("%n%d/%d PASS%n", passed, total); + if (passed < total) System.exit(1); + } +} diff --git a/whitepaper/MD5SUMS b/whitepaper/MD5SUMS index 660bad8ad..284801d06 100644 --- a/whitepaper/MD5SUMS +++ b/whitepaper/MD5SUMS @@ -3,7 +3,7 @@ ba0de5d1546aa2971492f74616f13f47 full-paper.pdf 3fda5736a004c621f52701c92a7ca7f5 undefect-cwe407-2026-03-24.pdf f076f22e9e70a94f51884562aad6fdc5 undefect-cwe407-2026-03-25.pdf 5da33a4087fdca81f70cce84656afc7f undefect-cwe407-2026-03-26.pdf -791d5fd391e5f6186f59848004fcf204 undefect-cwe407-2026-03-27.pdf +87845a2ecd45c1b032b30e44bff2292f undefect-cwe407-2026-03-27.pdf ff52abf9f47a7e6bb25e4519b1325090 undefect-minecraft-enterprise-java-2026-03-24.pdf c7fe499eb004271b384a31ac01b38852 undefect-minecraft-enterprise-java-2026-03-25.pdf 818d29731df88333d29cfdd3eefeb3a2 undefect-minecraft-enterprise-java-2026-03-26.pdf diff --git a/whitepaper/full-paper.md b/whitepaper/full-paper.md index 842a9ac48..8087e8d3e 100644 --- a/whitepaper/full-paper.md +++ b/whitepaper/full-paper.md @@ -39,7 +39,7 @@ A single well-crafted implementation serves as the genetic blueprint. 4. **Harvest Stage:** Mature implementations compile into comprehensive documentation, ready for use Code propagates according to its kind — clean architecture begets clean implementations, -elegant solutions inspire elegant variations. The process of generating 603 validated +elegant solutions inspire elegant variations. The process of generating 606 validated defect patches across 240 ecosystems in a single research wave demonstrates how truth, properly seeded, multiplies. Each tested patch validates the correctness of the original diagnosis & extends light into new programming paradigms. @@ -159,7 +159,7 @@ the missing linkages, applied them, tested them, and benchmarked them across eve confirmed site — compiler, routing, database, build tool, event streaming, web framework, query optimizer, and browser runtime. -**603 sites patched. 3 deferred (PostgreSQL -0001/-0005; MongoDB -0005 IndexBounds). +**606 sites patched. 3 deferred (PostgreSQL -0001/-0005; MongoDB -0005 IndexBounds). 1 fixable-upstream (Erlang OTP). 1 fixable-pending (swipl-0003). 2 not-worth-fixing. 3 unpatched (Minecraft, Create mod). No language left behind. @@ -432,6 +432,9 @@ stacks, Spark schemas — this is the dominant build cost. | tor-0002 | Tor | `nodelist.c:2337` — `nodelist_add_node_and_family()` `smartlist_contains_string` O(N×F²) total; fix: pre-built `strmap` (significant) | **PATCHED** | | tor-0003 | Tor | `scheduler_kist.c` — `KIST_scheduler_on_channel_has_waiting_work()` `smartlist_contains` O(S) per channel notification; fix: `channel_t.in_scheduler_set` flag | **PATCHED** | | curl-0001 | curl | `lib/cookie.c` — `replace_existing()` O(C²) linked-list scan per cookie bucket insert; fix: per-bucket `HashMap` | **PATCHED** | +| curl-0002 | curl | `lib/transfer.c:85` — `Curl_checkheaders()` O(H) slist scan called K≈20 times per HTTP request → O(K×H); fix: `HashMap` built at `CURLOPT_HTTPHEADER` (500×) | **PATCHED** | +| curl-0003 | curl | `lib/hsts.c:225,389` — `Curl_hsts()` O(N) llist scan in `hsts_load` dedup (O(N²) file load) + per-request HTTPS upgrade check; fix: `HashMap` (499×) | **PATCHED** | +| libevent-0001 | libevent | `http.c:3697,4290` — `evhttp_dispatch_callback()` O(C) TAILQ scan per request + `evhttp_set_cb()` O(C²) setup dedup; fix: `HashMap` alongside TAILQ (200×) | **PATCHED** | | systemd-0001 | systemd | `src/basic/strv.c` — `strv_extend_strv(filter_duplicates=true)` calls `strv_contains()` O(N) per element, O(N²) total dedup; fix: pre-built hash set (249-749×) | **PATCHED** | | systemd-0002 | systemd | `src/shared/install.c` — `unit_file_get_list()` `strv_contains(states)` O(S) per unit file in `FOREACH_DIRENT` loop; O(U×S) total; fix: hash set before loop (5-10×) | **PATCHED** | | julia-0001 | Julia | `base/loading.jl:2102` — `isrelocatable()` `includes_srcfiles Vector` O(n) scan per include; O(n²) total; fix: `Set{CacheHeaderIncludes}` before loop (500×) | **PATCHED** | @@ -883,7 +886,7 @@ where D is the depth of the diamond chain. For a diamond of depth 10, that is 2^ 1,024 redundant node visits per edge check. Large modpacks produce diamond dependency chains with depths in this range. -**603 sites patched. 3 deferred (PostgreSQL -0001/-0005; MongoDB -0005 IndexBounds). 1 fixable-upstream (Erlang OTP — sltab patch). 1 fixable-pending (swipl-0003 attr_unify_hook). 2 not-worth-fixing. 3 unpatched (Minecraft, Create mod). 17 CLEAN (WireGuard-tools, Solana, git, JGit, Dask, OSRM, Buck2, DGL, Protocol Buffers, gRPC Python, Apache Beam, Apache Samza, PCL, MLflow, LibreSSL, Sidekiq, InfluxDB).** +**606 sites patched. 3 deferred (PostgreSQL -0001/-0005; MongoDB -0005 IndexBounds). 1 fixable-upstream (Erlang OTP — sltab patch). 1 fixable-pending (swipl-0003 attr_unify_hook). 2 not-worth-fixing. 3 unpatched (Minecraft, Create mod). 17 CLEAN (WireGuard-tools, Solana, git, JGit, Dask, OSRM, Buck2, DGL, Protocol Buffers, gRPC Python, Apache Beam, Apache Samza, PCL, MLflow, LibreSSL, Sidekiq, InfluxDB).** --- diff --git a/whitepaper/undefect-cwe407-2026-03-27.pdf b/whitepaper/undefect-cwe407-2026-03-27.pdf index 95183134a..11cc2f45d 100644 Binary files a/whitepaper/undefect-cwe407-2026-03-27.pdf and b/whitepaper/undefect-cwe407-2026-03-27.pdf differ