java-topology/defects/curl/unit/CurlMimeBuildHeaderSearchTest.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

129 lines
4.9 KiB
Java
Raw Permalink 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.*;
/**
* curl-0004 — CWE-407: curl_mime search_header O(P×H) per multipart request build
*
* Models lib/mime.c search_header() called 3× per MIME part in mime_add_headers():
* Slow: for each part, walk user headers singly-linked list 3× → O(P × H)
* Fast: pre-indexed header name set → O(P) total
*
* Trigger: curl_mime_formadd with many parts each having custom userheaders.
*/
public class CurlMimeBuildHeaderSearchTest {
// Simulates struct curl_slist (singly-linked list of strings)
static class CurlSlist {
final String data;
CurlSlist next;
CurlSlist(String data) { this.data = data; }
}
// Simulates curl_mimepart
static class CurlMimePart {
CurlSlist userheaders; // linked list of user-supplied headers
Set<String> headerNameCache; // fast: pre-built from userheaders
}
// Append to slist
static CurlSlist slistAppend(CurlSlist head, String data) {
if (head == null) return new CurlSlist(data);
CurlSlist cur = head;
while (cur.next != null) cur = cur.next;
cur.next = new CurlSlist(data);
return head;
}
// Build a mime part with H user headers
static CurlMimePart buildPart(int numHeaders, String[] fixedHeaders) {
CurlMimePart part = new CurlMimePart();
for (int i = 0; i < numHeaders; i++) {
String hdr = (i < fixedHeaders.length)
? (fixedHeaders[i] + ": value" + i)
: ("X-Custom-" + i + ": value" + i);
part.userheaders = slistAppend(part.userheaders, hdr);
}
return part;
}
// Simulate search_header: O(H) scan - returns true if header name found
static boolean searchHeaderSlow(CurlSlist hdrlist, String headerName, long[] opsOut) {
String target = headerName.toLowerCase() + ":";
for (CurlSlist h = hdrlist; h != null; h = h.next) {
opsOut[0]++;
if (h.data.toLowerCase().startsWith(target)) return true;
}
return false;
}
// Simulate fast header check: O(1) set lookup
static boolean searchHeaderFast(Set<String> cache, String headerName, long[] opsOut) {
opsOut[0]++;
return cache.contains(headerName.toLowerCase());
}
// Pre-build header name cache for a part
static Set<String> buildHeaderCache(CurlSlist userheaders) {
Set<String> cache = new HashSet<>();
for (CurlSlist h = userheaders; h != null; h = h.next) {
int colon = h.data.indexOf(':');
if (colon > 0) cache.add(h.data.substring(0, colon).toLowerCase().trim());
}
return cache;
}
// Simulate mime_add_headers calling search_header 3× per part
static long mimeAddHeadersSlow(List<CurlMimePart> parts) {
long[] ops = { 0 };
String[] checks = { "Content-Type", "Content-Disposition", "Content-Transfer-Encoding" };
for (CurlMimePart part : parts) {
for (String check : checks)
searchHeaderSlow(part.userheaders, check, ops);
}
return ops[0];
}
static long mimeAddHeadersFast(List<CurlMimePart> parts) {
long[] ops = { 0 };
String[] checks = { "Content-Type", "Content-Disposition", "Content-Transfer-Encoding" };
for (CurlMimePart part : parts) {
// Build cache once per part (amortized O(H) for the part, O(1) per lookup)
if (part.headerNameCache == null)
part.headerNameCache = buildHeaderCache(part.userheaders);
for (String check : checks)
searchHeaderFast(part.headerNameCache, check, ops);
}
return ops[0];
}
public static void main(String[] args) {
System.out.println("curl-0004 CWE-407: curl_mime search_header O(P*H) vs O(P)");
System.out.println("===========================================================");
String[] knownHeaders = { "Content-Type", "Content-Disposition", "Content-Transfer-Encoding",
"Content-ID", "Content-Description" };
int[][] params = { {10, 10}, {50, 20}, {100, 30} };
for (int[] p : params) {
int P = p[0], H = p[1];
List<CurlMimePart> parts = new ArrayList<>();
for (int i = 0; i < P; i++)
parts.add(buildPart(H, knownHeaders));
long slowOps = mimeAddHeadersSlow(parts);
long fastOps = mimeAddHeadersFast(parts);
double ratio = (double) slowOps / fastOps;
System.out.printf(" P=%3d parts, H=%2d headers/part: slow=%,6d ops fast=%,5d ops speedup=%.1fx%n",
P, H, slowOps, fastOps, ratio);
// Fast should be ~H/3 times cheaper (3 lookups but we only scan up to H headers)
assert slowOps > fastOps :
"Expected slow > fast but slow=" + slowOps + " fast=" + fastOps;
}
System.out.println("\nPASS");
}
}