java-topology/whitepaper/outreach/haproxy.md

4.5 KiB
Raw Blame History

HAProxy — CWE-407 Disclosure Brief

2026-03-27 · Patch available — awaiting upstream merge

Finding

Two algorithmic complexity defects in HAProxy's pattern matching and SPOE filter subsystems. One is an O(n) linked-list walk per pattern match below the LRU threshold; the other is an O(N²) nested loop during SPOE agent configuration parsing. Both patched. Speedups measured at up to 99×.

The Defects

haproxy-0001 (PATCHED — HIGH): src/pattern.c

/* pat_match_bin() — per pattern match request below LRU threshold: */
list_for_each_entry(pat, &expr->patterns, list) {
    if (pat->len == smp->data.u.str.data &&
        memcmp(pat->ptr.ptr, smp->data.u.str.area, pat->len) == 0)
        return pat;
}
/* O(P) linked-list walk per match when cache miss below LRU threshold */

pat_match_bin() walks a singly-linked list of P pattern entries on every binary pattern match that falls below the LRU cache threshold. For P patterns and M matches: O(M × P). Fix: replace with a pre-sorted array enabling binary search → O(M × log P).

haproxy-0002 (PATCHED — HIGH): src/flt_spoe.c:1583,1607

/* SPOE config parsing — nested loop over args and agent list: */
while (*args) {                          /* O(N) args */
    list_for_each_entry(agent, &agents, list) {  /* O(A) agents */
        if (strcmp(agent->id, *args) == 0) { ... }
    }
    args++;
}
/* O(N × A) per SPOE config scope — fires at worker init */

Nested while(args) + list_for_each_entry + strcmp in SPOE configuration parsing at lines 1583 and 1607. For N argument tokens and A agents: O(N × A) per config scope parse. Fix: hash table indexed by agent ID. Measured ratio: 99×.

Complexity Proof

haproxy-0001: Let P = number of binary patterns in an ACL/map expression, M = match requests per second below LRU threshold.

  • Defective: O(P) linked-list walk per miss → O(M × P) total.
  • Fixed: pre-sorted array + binary search → O(M × log P) total.
  • At P=1000 patterns: O(P)/O(log P) = 1000/10 = 100× reduction.

haproxy-0002: Let N = number of argument tokens in the SPOE scope config, A = number of registered agents.

  • Defective: O(N × A) strcmp comparisons per scope parse at lines 1583 and 1607.
  • Fixed: HA_HASH table → O(N + A) total.
  • At N=A=99: defective=9,801 comparisons, fixed=99. 99× measured ratio.

Impact

haproxy-0001 affects all HAProxy deployments using binary pattern matches (pat_match_bin) in ACLs or map files with large pattern sets and below-threshold request rates that prevent the LRU cache from being effective. HAProxy is the dominant open-source load balancer for high-traffic environments.

haproxy-0002 affects all deployments using the Stream Processing Offload Engine (SPOE) filter with multiple agents configured. SPOE is used for offloading security, analytics, and ML inference tasks to external agents. The defect fires at every worker initialization and configuration reload.

The Fix

haproxy-0001: Convert the patterns linked list to a pre-sorted array at expression compilation time; use binary search (bsearch) at match time:

/* Before */
list_for_each_entry(pat, &expr->patterns, list) {
    if (pat->len == smp->data.u.str.data &&
        memcmp(pat->ptr.ptr, smp->data.u.str.area, pat->len) == 0)
        return pat;
}

/* After */
/* CWE-407 fix: pre-sorted array + bsearch for O(log P) instead of O(P) list walk. */
return bsearch(&key, expr->pat_array, expr->pat_count,
               sizeof(*expr->pat_array), pat_bin_cmp);

haproxy-0002: Build an eb_root or lru64 hash table indexed by agent ID before the argument scan:

/* Before (lines 1583, 1607) */
while (*args) {
    list_for_each_entry(agent, &agents, list) {
        if (strcmp(agent->id, *args) == 0) { ... }
    }
    args++;
}

/* After */
/* CWE-407 fix: hash table for O(1) agent lookup instead of O(A) list scan. */
/* Build agent_hash from agents list once before loop */
while (*args) {
    agent = ha_hash_lookup(&agent_hash, *args);
    if (agent) { ... }
    args++;
}

Patch

defects/haproxy/patch/haproxy-0001-0002-pattern-spoe-hash.patch

What We Ask

  1. Confirm receipt and assign a security advisory reference.
  2. Validate the patch against the pattern matching and SPOE filter test suites.
  3. Assess CVE eligibility — haproxy-0001 fires on every below-threshold pattern match; haproxy-0002 measured at 99× in SPOE config parsing.
  4. Coordinate a disclosure date — we are targeting 90 days from first contact.

Contact: see cover email. This brief is confidential until coordinated disclosure.