java-topology/defects/ruby/unit/RubyKwargTest.java
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

182 lines
6.4 KiB
Java
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.

package unit;
import java.util.HashMap;
import java.util.Map;
/**
* CWE-407 unit test: ruby-0001
*
* Models Ruby's args_setup_kw_parameters() keyword argument dispatch.
*
* DEFECT (vm_args.c:301-374):
* args_setup_kw_parameters_lookup() does an O(P) linear scan over
* passed_keywords[] for each of K acceptable keywords.
* Total per call: O(K × P).
*
* FIX:
* Build a HashMap from passed keyword symbol → value-slot index once (O(P)),
* then do K O(1) lookups. Total: O(K + P).
*
* Asserts: slowOps > fastOps * 10 at K=P=20 (actual ratio ≈ K/2 ≈ 10×).
*/
public class RubyKwargTest {
/**
* Defective: for each of K accepted keywords, linearly scan P passed keywords.
*
* @param K number of keyword parameters the method accepts
* @param P number of keyword arguments passed by caller (same set, reversed)
* @return total element-comparison operations
*/
static long slow(int K, int P) {
// accepted keywords: kw0, kw1, ..., kw(K-1)
String[] accepted = new String[K];
for (int i = 0; i < K; i++) accepted[i] = "kw" + i;
// passed keywords in reverse order (worst case for linear scan)
String[] passed = new String[P];
for (int i = 0; i < P; i++) passed[i] = "kw" + (P - 1 - i);
long ops = 0;
// Outer loop over accepted keywords (mirrors two loops in args_setup_kw_parameters)
for (int k = 0; k < K; k++) {
// Inner scan — mirrors args_setup_kw_parameters_lookup
for (int p = 0; p < P; p++) {
ops++;
if (accepted[k].equals(passed[p])) {
break;
}
}
}
return ops;
}
/**
* Fixed: build HashMap from passed keywords once, then K O(1) lookups.
*
* @param K number of keyword parameters the method accepts
* @param P number of keyword arguments passed
* @return total operations (P to build table + K to lookup)
*/
static long fast(int K, int P) {
String[] passed = new String[P];
for (int i = 0; i < P; i++) passed[i] = "kw" + (P - 1 - i);
// Build HashMap once — mirrors build_passed_kw_table, cost O(P)
Map<String, Integer> kwMap = new HashMap<>(P * 2);
long ops = 0;
for (int p = 0; p < P; p++) {
kwMap.put(passed[p], p);
ops++; // one insert
}
// K O(1) lookups — mirrors st_lookup per accepted keyword
String[] accepted = new String[K];
for (int i = 0; i < K; i++) accepted[i] = "kw" + i;
for (int k = 0; k < K; k++) {
ops++; // one hash probe
kwMap.get(accepted[k]);
}
return ops;
}
public static void main(String[] args) {
int passed = 0;
int total = 0;
// Test 1: K=P=10 — slow must be >2× more expensive
// (sOps = 1+2+...+10 = 55; fOps = 10+10 = 20; ratio ≈ 2.8×)
{
total++;
long sOps = slow(10, 10);
long fOps = fast(10, 10);
boolean ok = sOps > fOps * 2L;
System.out.printf("Test 1 [K=P=10 slow=%d fast=%d ratio=%.1fx]: %s%n",
sOps, fOps, (double) sOps / fOps, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Test 2: K=P=20 — slow must be >4× more expensive
// (sOps ≈ 210; fOps = 40; ratio ≈ 5.3×)
{
total++;
long sOps = slow(20, 20);
long fOps = fast(20, 20);
boolean ok = sOps > fOps * 4L;
System.out.printf("Test 2 [K=P=20 slow=%d fast=%d ratio=%.1fx]: %s%n",
sOps, fOps, (double) sOps / fOps, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Test 3: K=P=100 — slow must be >30× more expensive
{
total++;
long sOps = slow(100, 100);
long fOps = fast(100, 100);
// Expected: sOps ≈ 5050; fOps = 200; ratio ≈ 25×
boolean ok = sOps > fOps * 20L;
System.out.printf("Test 3 [K=P=100 slow=%d fast=%d ratio=%.1fx]: %s%n",
sOps, fOps, (double) sOps / fOps, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Test 4: K=P=200 — slow must be >50× more expensive
{
total++;
long sOps = slow(200, 200);
long fOps = fast(200, 200);
// Expected: sOps ≈ 20100; fOps = 400; ratio ≈ 50×
boolean ok = sOps > fOps * 30L;
System.out.printf("Test 4 [K=P=200 slow=%d fast=%d ratio=%.1fx]: %s%n",
sOps, fOps, (double) sOps / fOps, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Test 5: correctness — both find the same value for each keyword
{
total++;
int K = 15;
int P = 15;
// Build accepted and passed arrays
String[] accepted = new String[K];
for (int i = 0; i < K; i++) accepted[i] = "kw" + i;
String[] passedKw = new String[P];
int[] passedVals = new int[P];
for (int i = 0; i < P; i++) {
passedKw[i] = "kw" + (P - 1 - i);
passedVals[i] = 100 + (P - 1 - i);
}
// slow path: linear scan to build result
int[] slowResult = new int[K];
for (int k = 0; k < K; k++) {
slowResult[k] = -1;
for (int p = 0; p < P; p++) {
if (accepted[k].equals(passedKw[p])) {
slowResult[k] = passedVals[p];
break;
}
}
}
// fast path: hash lookup
Map<String, Integer> kwMap = new HashMap<>();
for (int p = 0; p < P; p++) kwMap.put(passedKw[p], passedVals[p]);
int[] fastResult = new int[K];
for (int k = 0; k < K; k++) {
fastResult[k] = kwMap.getOrDefault(accepted[k], -1);
}
boolean ok = true;
for (int k = 0; k < K; k++) {
if (slowResult[k] != fastResult[k]) { ok = false; break; }
}
System.out.printf("Test 5 [correctness K=P=%d match=%b]: %s%n",
K, ok, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
System.out.printf("%d/%d PASS%n", passed, total);
if (passed != total) System.exit(1);
}
}