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
182 lines
6.4 KiB
Java
182 lines
6.4 KiB
Java
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);
|
||
}
|
||
}
|