wave17: 562/240 — vim/qemu/tcl/nodejs/bun/httpd-0002/systemd/emacs
This commit is contained in:
parent
cce7ec653a
commit
dde5ec97fb
13 changed files with 887 additions and 4 deletions
|
|
@ -0,0 +1,72 @@
|
|||
# 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:
|
||||
|
||||
```c
|
||||
/* 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.
|
||||
|
||||
```c
|
||||
/* 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue