1.6 KiB
1.6 KiB
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:
// 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:
-
File load (
hsts_load→hsts_add→Curl_hstsfor dedup): N calls, each O(N) → O(N²) total. A 1,000-entry HSTS file performs 500,000 comparisons. -
Per-request HTTPS upgrade (
Curl_hsts_parselines 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):
// 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).