java-topology/docs/tickets/curl-0001-cookie-replace-existing-llist-quadratic.md
russell@unturf.com 9934133dcf whitepaper: 312 sites / 151 ecosystems — wave2+3 defect tables and PDF rebuild
Add 88 new defect entries to HIGH and MEDIUM tables:
  HIGH: mysql-0001/0002, mariadb-0001, redis-0001/0002, valkey-0001/0002, openvpn-0001,
        vlc-0001, prometheus-0001, otel-collector-0001, cockroachdb-0001..0004,
        tidb-0001..0008, kubernetes-0001/0002, go-0001, kotlin-0002, scala-0001,
        allegro5-0001, sdl2-0001, grafana-0001, clickhouse-0001, duckdb-0001,
        mongodb-0001, envoy-0001, istio-0001, cilium-0001, linkerd2-0001,
        linux-0001/0002/0003, tor-0002/0003, curl-0001, julia-0001, lua-0001,
        perl5-0001, nats-0001, spring-0003/0004, tomcat-0001, onos-0002, odl-0002

  MEDIUM: helm-0001, mariadb-0002, openssl-0001/0002, memcached-0001,
          cassandra-0001..0004, flink-0001, storm-0001/0002, zookeeper-0001..0003,
          pip-0001, gradle-0001, nginx-0001, haproxy-0001, caddy-0001, varnish-0001,
          ffmpeg-0001, gstreamer-0001, raylib-0001, love2d-0001, php-0001/0002,
          r-source-0001, cpython-0002, ruby-0001, rabbitmq-0003/0004, activemq-0001,
          ovs-0001, onos-0003, odl-0002, jetty-0001

PDF: 976K
2026-03-27 15:23:43 -04:00

2.1 KiB

curl-0001: Curl_cookie_add replace_existing — O(C²) linked-list scan per same-domain cookie

Target: curl File: lib/cookie.c Function: replace_existing (called from Curl_cookie_add) CWE: CWE-407 — Inefficient Algorithmic Complexity Severity: MEDIUM Status: PATCHED

Description

Curl_cookie_add() stores cookies in 63 hash buckets keyed by the top-level domain (COOKIE_HASH_SIZE = 63). Before inserting a new cookie it calls replace_existing(), which walks the entire linked list of the target bucket to find and remove a matching (name, domain, path) cookie:

// cookie.c  line 831-832 (replace_existing)
size_t myhash = cookiehash(co->domain);
for(n = Curl_llist_head(&ci->cookielist[myhash]); n; n = Curl_node_next(n)) {
    struct Cookie *clist = Curl_node_elem(n);
    if(!strcmp(clist->name, co->name)) {   // O(C/63) per call
        ...
    }
}

Because the bucket size is fixed at 63, all cookies from the same domain land in the same bucket. For C cookies sharing one domain the cost per Curl_cookie_add call is O(C) — scanning the entire bucket. Over C insertions that is O(C²).

Real-world exposure: a response that sets many cookies for one domain (e.g. large JAR files, test harnesses, or adversarial servers) triggers quadratic work in the client. A site setting 10 000 per-path cookies (within MAX_COOKIE_SEND_AMOUNT limits) costs ~50 M string comparisons.

Fix

Maintain a secondary Curl_hash keyed by name within CookieInfo, mapping name → Curl_llist_node *. Before walking the bucket, look up the candidate node in O(1); walk only if the hash indicates a potential match. Alternatively, switch the per-bucket structure from a linked list to a hash table keyed by (name, domain, path).

Minimal patch: add a Curl_hash name_index field to CookieInfo, populated at insert time and invalidated at remove time, so that replace_existing performs a single hash lookup instead of a full list scan.

Patch

defects/curl/patch/curl-0001-cookie-name-hash-index.patch

Unit Test

defects/curl/unit/CurlCookieReplaceTest.java