java-topology/docs/tickets/ruby-0001-kwarg-setup-linear-scan-quadratic.md
russell@unturf.com 9934133dcf whitepaper: 312 sites / 151 ecosystems — wave2+3 defect tables and PDF rebuild
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
2026-03-27 15:23:43 -04:00

76 lines
2.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 | 300374 |
| 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× |