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

2.2 KiB
Raw Blame History

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:

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:

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

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