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
76 lines
2.2 KiB
Markdown
76 lines
2.2 KiB
Markdown
# ruby-0001 — vm_args.c: O(n²) linear keyword-argument matching per call
|
||
|
||
| Field | Value |
|
||
|-------|-------|
|
||
| ID | ruby-0001 |
|
||
| Target | Ruby |
|
||
| File | `vm_args.c` |
|
||
| Lines | 300–374 |
|
||
| CWE | CWE-407 (Algorithmic Complexity) |
|
||
| Severity | HIGH |
|
||
| Status | PATCHED |
|
||
|
||
## Description
|
||
|
||
`args_setup_kw_parameters()` maps passed keyword arguments to formal parameter
|
||
slots. For each of the function's `key_num` acceptable keywords it calls
|
||
`args_setup_kw_parameters_lookup()`, which linearly scans the full
|
||
`passed_keyword_len` array of passed keyword names:
|
||
|
||
```c
|
||
static inline int
|
||
args_setup_kw_parameters_lookup(const ID key, VALUE *ptr,
|
||
const VALUE *const passed_keywords, VALUE *passed_values,
|
||
const int passed_keyword_len)
|
||
{
|
||
int i;
|
||
const VALUE keyname = ID2SYM(key);
|
||
for (i=0; i<passed_keyword_len; i++) { // O(passed_keyword_len)
|
||
if (keyname == passed_keywords[i]) {
|
||
...
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
```
|
||
|
||
Called from two loops in `args_setup_kw_parameters`:
|
||
|
||
```c
|
||
for (i=0; i<req_key_num; i++) {
|
||
args_setup_kw_parameters_lookup(acceptable_keywords[i], ...); // O(P)
|
||
}
|
||
for (di=0; i<key_num; i++, di++) {
|
||
args_setup_kw_parameters_lookup(acceptable_keywords[i], ...); // O(P)
|
||
}
|
||
```
|
||
|
||
Total cost: O(key_num × passed_keyword_len). For a method with K keyword
|
||
params called with P keyword arguments, every call is O(K × P).
|
||
|
||
## Reproduction
|
||
|
||
```ruby
|
||
# Method with 20 keyword params, called with all 20 named
|
||
def wide(a0:, a1:, a2:, a3:, a4:, a5:, a6:, a7:, a8:, a9:,
|
||
b0:, b1:, b2:, b3:, b4:, b5:, b6:, b7:, b8:, b9:) end
|
||
# Each call: 20 acceptable keywords × 20 passed = 400 linear comparisons
|
||
# vs hash: 20 + 20 = 40 operations
|
||
```
|
||
|
||
## Fix
|
||
|
||
Build a temporary `st_table` (Ruby's hash) from `passed_keywords` → index
|
||
once before the two loops, then replace `args_setup_kw_parameters_lookup` with
|
||
an O(1) `st_lookup`. Build cost: O(P). Total: O(K + P).
|
||
|
||
## Complexity
|
||
|
||
| Metric | Before | After |
|
||
|--------|--------|-------|
|
||
| Per call (K params, P passed) | O(K × P) | O(K + P) |
|
||
| K=P=20 | 400 comparisons | 40 ops |
|
||
| Speedup (K=P=20) | 1× | ~10× |
|
||
| K=P=100 | 10 000 comparisons | 200 ops |
|
||
| Speedup (K=P=100) | 1× | ~50× |
|