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)
This commit is contained in:
russell@unturf.com 2026-03-29 22:22:11 -04:00
parent 421f3352c7
commit ba818693db
21 changed files with 2213 additions and 0 deletions

View file

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