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
2 KiB
php-0001 — zend_compile.c: O(n²) linear scan for named argument position
| Field | Value |
|---|---|
| ID | php-0001 |
| Target | PHP |
| File | Zend/zend_compile.c |
| Lines | 3755–3766, 3784, 3822 |
| CWE | CWE-407 (Algorithmic Complexity) |
| Severity | HIGH |
| Status | PATCHED |
Description
zend_get_arg_num() performs a linear scan over fn->common.num_args entries
to locate a named argument by string comparison:
static uint32_t zend_get_arg_num(const zend_function *fn, const zend_string *arg_name) {
// TODO: Caching?
for (uint32_t i = 0; i < fn->common.num_args; i++) {
zend_arg_info *arg_info = &fn->op_array.arg_info[i];
if (zend_string_equals(arg_info->name, arg_name)) {
return i + 1;
}
}
return (uint32_t) -1;
}
This is called from zend_compile_args() which iterates over all args->children
at compile time. For a function with M parameters called with N named arguments
the total cost is O(N × M). The upstream comment // TODO: Caching? acknowledges
the defect.
The companion runtime function zend_get_arg_offset_by_name() in
zend_execute.c (line 5479) carries the same structure with an explicit
// TODO: Use a hash table? comment, and is subject to identical quadratic
behavior on cache miss.
Reproduction
// Function with 50 named parameters, called with all 50 named — 50×50 = 2500 scans
function wide(
$p0, $p1, $p2, ..., $p49
) {}
// Each named arg triggers zend_get_arg_num linear scan over 50 entries
wide(p49: 1, p48: 2, ..., p0: 50);
Fix
Build a HashTable from arg_name → position once per function signature and
cache it on the zend_function (or use the existing per-opcode cache slot for
the runtime path, which already has infrastructure but is not used for the
compile-time path).
Complexity
| Metric | Before | After |
|---|---|---|
| Per named-arg lookup | O(M) | O(1) |
| N named args, M params | O(N×M) | O(N + M) |
| Speedup (N=M=50) | 1× | ~50× |