java-topology/defects/httpd/patch/httpd-0002-proxy-noproxy-dirconn-dedup.md

2.4 KiB

UNDF: UNDF-2026-000000418

httpd-0002 — mod_proxy NoProxy/DirectConnect config-parse O(N²) dedup

Metadata

Field Value
ID httpd-0002
Severity MEDIUM
Component modules/proxy/mod_proxy.c
Functions set_proxy_exclude, set_proxy_dirconn
Complexity O(N²) config-parse time
CWE CWE-407 Inefficient Algorithmic Complexity
Status PATCHED

Description

set_proxy_exclude (handler for NoProxy directives) and set_proxy_dirconn (handler for ProxyDirectConnect directives) each perform a full linear scan of the existing array before inserting a new entry, to avoid duplicates:

/* set_proxy_exclude — modules/proxy/mod_proxy.c */
for (i = 0; i < conf->noproxies->nelts; i++) {
    if (strcasecmp(arg, list[i].name) == 0) {
        found = 1;
        break;
    }
}

With N directives in httpd.conf the total comparison count is: 0 + 1 + 2 + … + (N-1) = N(N-1)/2 = O(N²)

Both functions share the same pattern. In typical deployments N is small (< 20), but in automated config generation, container orchestration, or large reverse-proxy farms the count can reach hundreds or thousands.

Impact

  • Config parse time (not request time): server startup / graceful reload is slower than necessary when many NoProxy or ProxyDirectConnect directives are present.
  • A generated config with 1 000 entries does ~500 000 strcasecmp calls instead of ~1 000 hash probes.

Root Cause

APR arrays have no built-in set semantics; the dedup loop is the standard APR idiom but was never replaced with a hash table as the directive lists grew.

Fix

Replace the APR array + linear dedup with an apr_hash_t keyed on the lowercased hostname/entry string. The array can be kept for ordered iteration at request time; the hash is used only during config parsing for O(1) dedup.

/* Proposed replacement in set_proxy_exclude */
apr_hash_t *noproxy_set = ap_get_noproxy_set(conf, parms->pool);
char *lower = apr_pstrdup(parms->pool, arg);
ap_str_tolower(lower);
if (apr_hash_get(noproxy_set, lower, APR_HASH_KEY_STRING)) {
    return NULL;  /* duplicate — skip */
}
apr_hash_set(noproxy_set, lower, APR_HASH_KEY_STRING, (void *)1);
/* then push to conf->noproxies as before */

Measured Overhead

See unit test HttpdProxyNoproxyTest.java:

  • N=500 entries: SLOW=124 750 ops, FAST=500 ops, ratio=249.5x