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
445 lines
20 KiB
Java
445 lines
20 KiB
Java
package unit;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* RabbitMQQueueTest — Java model of CWE-407 defects in RabbitMQ.
|
||
*
|
||
* RMQ-001 (MEDIUM): rabbit_classic_queue — pending pids stored as List.
|
||
* Defective: Map<SeqNo, List<Pid>> → lists:member() O(P) per message on DOWN.
|
||
* Fixed: Map<SeqNo, Set<Pid>> → Set.contains() O(1) per message.
|
||
*
|
||
* RMQ-002 (LOW): rabbit_stream_sac_coordinator — is_pid_alive rebuilds node
|
||
* member list and calls lists:member() per consumer pid.
|
||
* Defective: List<Node> rebuilt + scanned per consumer → O(N × C).
|
||
* Fixed: HashSet<Node> hoisted outside filter → O(N + C × log N).
|
||
*/
|
||
public class RabbitMQQueueTest {
|
||
|
||
// -----------------------------------------------------------------------
|
||
// RMQ-001 helpers
|
||
// -----------------------------------------------------------------------
|
||
|
||
/** Defective: pending stored as List — O(P) contains per message. */
|
||
static long rmq001Defective(int messages, int pids, int targetPid) {
|
||
Map<Integer, List<Integer>> unconfirmed = new HashMap<>();
|
||
List<Integer> pendingList = new ArrayList<>();
|
||
for (int p = 0; p < pids; p++) pendingList.add(p);
|
||
|
||
for (int m = 0; m < messages; m++) {
|
||
unconfirmed.put(m, new ArrayList<>(pendingList));
|
||
}
|
||
|
||
long ops = 0;
|
||
// On publisher DOWN: filter all messages whose pending list contains targetPid
|
||
List<Integer> matched = new ArrayList<>();
|
||
for (Map.Entry<Integer, List<Integer>> e : unconfirmed.entrySet()) {
|
||
ops++;
|
||
if (e.getValue().contains(targetPid)) { // O(P) scan
|
||
matched.add(e.getKey());
|
||
}
|
||
}
|
||
return ops * pids; // actual comparisons proportional to M * P
|
||
}
|
||
|
||
/** Fixed: pending stored as Set — O(1) contains per message. */
|
||
static long rmq001Fixed(int messages, int pids, int targetPid) {
|
||
Map<Integer, Set<Integer>> unconfirmed = new HashMap<>();
|
||
Set<Integer> pendingSet = new HashSet<>();
|
||
for (int p = 0; p < pids; p++) pendingSet.add(p);
|
||
|
||
for (int m = 0; m < messages; m++) {
|
||
unconfirmed.put(m, new HashSet<>(pendingSet));
|
||
}
|
||
|
||
long ops = 0;
|
||
List<Integer> matched = new ArrayList<>();
|
||
for (Map.Entry<Integer, Set<Integer>> e : unconfirmed.entrySet()) {
|
||
ops++;
|
||
if (e.getValue().contains(targetPid)) { // O(1) hash lookup
|
||
matched.add(e.getKey());
|
||
}
|
||
}
|
||
return ops; // comparisons proportional to M only
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// RMQ-002 helpers — count actual list-scan comparisons via instrumentation
|
||
// -----------------------------------------------------------------------
|
||
|
||
/** Defective: member list rebuilt and scanned linearly per consumer pid. */
|
||
static long rmq002Defective(List<Integer> memberList, List<Integer> consumerNodes) {
|
||
long comparisons = 0;
|
||
for (Integer consumerNode : consumerNodes) {
|
||
// Rebuild the list every time (simulates rabbit_nodes:list_members() call)
|
||
List<Integer> members = new ArrayList<>(memberList);
|
||
// Linear scan (simulates lists:member/2)
|
||
boolean found = false;
|
||
for (Integer m : members) {
|
||
comparisons++;
|
||
if (m.equals(consumerNode)) { found = true; break; }
|
||
}
|
||
// worst-case: not found → full scan; simulate that
|
||
if (!found) comparisons += 0; // already counted above
|
||
}
|
||
return comparisons;
|
||
}
|
||
|
||
/** Fixed: HashSet hoisted outside the filter — O(1) per consumer. */
|
||
static long rmq002Fixed(List<Integer> memberList, List<Integer> consumerNodes) {
|
||
long comparisons = 0;
|
||
// Hoist: build set once outside loop
|
||
Set<Integer> memberSet = new HashSet<>(memberList);
|
||
for (Integer consumerNode : consumerNodes) {
|
||
comparisons++; // O(1) hash probe
|
||
memberSet.contains(consumerNode); // actual O(1) work
|
||
}
|
||
return comparisons;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Precise comparison-count model for RMQ-002 worst-case
|
||
// -----------------------------------------------------------------------
|
||
|
||
/**
|
||
* Models worst-case: all consumer nodes are NOT in the member list,
|
||
* forcing a full N-element scan per consumer in the defective path.
|
||
*/
|
||
static long rmq002DefectiveWorstCase(int nodeCount, int consumerCount) {
|
||
long comparisons = 0;
|
||
List<Integer> members = new ArrayList<>();
|
||
for (int i = 0; i < nodeCount; i++) members.add(i);
|
||
|
||
List<Integer> consumers = new ArrayList<>();
|
||
for (int c = 0; c < consumerCount; c++) consumers.add(nodeCount + c); // all absent
|
||
|
||
for (int c = 0; c < consumerCount; c++) {
|
||
// rebuild list per consumer (simulates list_members call)
|
||
for (int i = 0; i < nodeCount; i++) comparisons++; // full scan, not found
|
||
}
|
||
return comparisons; // N * C
|
||
}
|
||
|
||
static long rmq002FixedWorstCase(int nodeCount, int consumerCount) {
|
||
// set built once (N inserts), then C O(1) probes
|
||
return (long) nodeCount + consumerCount;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// RMQ-003 helpers — check_declare_arguments: lists:filter + lists:member
|
||
// -----------------------------------------------------------------------
|
||
|
||
/**
|
||
* Defective: for each validator in declare_args (D elements), call
|
||
* lists:member against queueTypeArgs (Q elements) — O(D * Q) comparisons.
|
||
*/
|
||
static long rmq003Defective(List<String> declareArgs, List<String> queueTypeArgs) {
|
||
long comparisons = 0;
|
||
List<String> validators = new ArrayList<>();
|
||
for (String arg : declareArgs) {
|
||
// lists:member scan
|
||
for (String qa : queueTypeArgs) {
|
||
comparisons++;
|
||
if (arg.equals(qa)) { validators.add(arg); break; }
|
||
}
|
||
}
|
||
return comparisons;
|
||
}
|
||
|
||
/**
|
||
* Fixed: convert queueTypeArgs to a Set first, then filter in O(D).
|
||
*/
|
||
static long rmq003Fixed(List<String> declareArgs, List<String> queueTypeArgs) {
|
||
long comparisons = 0;
|
||
Set<String> queueTypeArgsSet = new HashSet<>(queueTypeArgs);
|
||
List<String> validators = new ArrayList<>();
|
||
for (String arg : declareArgs) {
|
||
comparisons++;
|
||
if (queueTypeArgsSet.contains(arg)) { validators.add(arg); }
|
||
}
|
||
return comparisons;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// RMQ-004 helpers — check_arguments_key: lists:foreach + lists:member
|
||
// -----------------------------------------------------------------------
|
||
|
||
/**
|
||
* Defective: for each supplied arg (A elements), scan invalidArgs list (I elements) — O(A * I).
|
||
*/
|
||
static long rmq004Defective(List<String> suppliedArgs, List<String> invalidArgs) {
|
||
long comparisons = 0;
|
||
for (String arg : suppliedArgs) {
|
||
for (String inv : invalidArgs) {
|
||
comparisons++;
|
||
if (arg.equals(inv)) { break; }
|
||
}
|
||
}
|
||
return comparisons;
|
||
}
|
||
|
||
/**
|
||
* Fixed: build invalidArgs set once, then probe O(1) per arg — O(I + A).
|
||
*/
|
||
static long rmq004Fixed(List<String> suppliedArgs, List<String> invalidArgs) {
|
||
long comparisons = 0;
|
||
Set<String> invalidArgsSet = new HashSet<>(invalidArgs);
|
||
comparisons += invalidArgs.size(); // set construction: I insertions
|
||
for (String arg : suppliedArgs) {
|
||
comparisons++; // O(1) probe
|
||
invalidArgsSet.contains(arg);
|
||
}
|
||
return comparisons;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Test methods
|
||
// -----------------------------------------------------------------------
|
||
|
||
/**
|
||
* Test 1: RMQ-001 — comparison count ratio > 5x at M=500, P=10.
|
||
*/
|
||
static void test_rmq001_comparison_ratio() {
|
||
final int M = 500, P = 10, TARGET = 5;
|
||
long defectiveOps = rmq001Defective(M, P, TARGET);
|
||
long fixedOps = rmq001Fixed(M, P, TARGET);
|
||
|
||
double ratio = (double) defectiveOps / fixedOps;
|
||
System.out.printf(" RMQ-001 comparison ratio defective=%d fixed=%d ratio=%.1fx%n",
|
||
defectiveOps, fixedOps, ratio);
|
||
assert ratio > 5.0 : "RMQ-001: expected ratio > 5x, got " + ratio;
|
||
}
|
||
|
||
/**
|
||
* Test 2: RMQ-001 — defective ops scale as O(M*P), fixed ops scale as O(M).
|
||
* Verify that doubling P doubles defective cost but not fixed cost.
|
||
*/
|
||
static void test_rmq001_scaling_with_pids() {
|
||
final int M = 500, P_LO = 5, P_HI = 50, TARGET = 2;
|
||
long defLo = rmq001Defective(M, P_LO, TARGET);
|
||
long defHi = rmq001Defective(M, P_HI, TARGET);
|
||
long fixLo = rmq001Fixed(M, P_LO, TARGET);
|
||
long fixHi = rmq001Fixed(M, P_HI, TARGET);
|
||
|
||
double defScale = (double) defHi / defLo;
|
||
double fixScale = (double) fixHi / fixLo;
|
||
|
||
System.out.printf(" RMQ-001 P scaling defScale=%.1fx fixScale=%.1fx%n",
|
||
defScale, fixScale);
|
||
assert defScale > 5.0 : "RMQ-001: defective should scale with P, got " + defScale;
|
||
assert fixScale < 2.0 : "RMQ-001: fixed should not scale with P, got " + fixScale;
|
||
}
|
||
|
||
/**
|
||
* Test 3: RMQ-002 — comparison count ratio > 10x at N=20, C=100.
|
||
*/
|
||
static void test_rmq002_comparison_ratio() {
|
||
final int N = 20, C = 100;
|
||
long defectiveOps = rmq002DefectiveWorstCase(N, C);
|
||
long fixedOps = rmq002FixedWorstCase(N, C);
|
||
|
||
double ratio = (double) defectiveOps / fixedOps;
|
||
System.out.printf(" RMQ-002 comparison ratio defective=%d fixed=%d ratio=%.1fx%n",
|
||
defectiveOps, fixedOps, ratio);
|
||
assert ratio > 10.0 : "RMQ-002: expected ratio > 10x, got " + ratio;
|
||
}
|
||
|
||
/**
|
||
* Test 4: RMQ-002 — defective cost grows as N*C; fixed cost grows as N+C.
|
||
* Verify with N=20/C=200 vs N=20/C=100.
|
||
*/
|
||
static void test_rmq002_scaling_with_consumers() {
|
||
final int N = 20, C_LO = 50, C_HI = 500;
|
||
long defLo = rmq002DefectiveWorstCase(N, C_LO);
|
||
long defHi = rmq002DefectiveWorstCase(N, C_HI);
|
||
long fixLo = rmq002FixedWorstCase(N, C_LO);
|
||
long fixHi = rmq002FixedWorstCase(N, C_HI);
|
||
|
||
double defScale = (double) defHi / defLo;
|
||
double fixScale = (double) fixHi / fixLo;
|
||
|
||
System.out.printf(" RMQ-002 C scaling defScale=%.1fx fixScale=%.1fx%n",
|
||
defScale, fixScale);
|
||
// Defective: N*C_HI / N*C_LO = C_HI/C_LO = 10
|
||
assert defScale > 8.0 : "RMQ-002: defective should scale linearly with C, got " + defScale;
|
||
// Fixed: (N + C_HI) / (N + C_LO) ≈ (20+500)/(20+50) ≈ 7.4 but dominated by C
|
||
// The important assertion: fixed is always cheaper than defective
|
||
assert fixHi < defHi : "RMQ-002: fixed should be cheaper than defective at high C";
|
||
}
|
||
|
||
/**
|
||
* Test 5: RMQ-003 — check_declare_arguments validator filter.
|
||
* D=21 declare_args, Q=8 queue-type args, worst-case: no matches → full scan.
|
||
* Defective: O(D*Q) = 168 comparisons. Fixed: O(D) = 21.
|
||
*/
|
||
static void test_rmq003_comparison_ratio() {
|
||
final int D = 21, Q = 8;
|
||
List<String> declareArgs = new ArrayList<>();
|
||
for (int i = 0; i < D; i++) declareArgs.add("x-arg-" + i);
|
||
List<String> queueTypeArgs = new ArrayList<>();
|
||
// Q args that are NOT in declareArgs → worst-case full scan per element
|
||
for (int i = 0; i < Q; i++) queueTypeArgs.add("x-type-" + i);
|
||
|
||
long sOps = rmq003Defective(declareArgs, queueTypeArgs);
|
||
long fOps = rmq003Fixed(declareArgs, queueTypeArgs);
|
||
|
||
double ratio = (double) sOps / fOps;
|
||
System.out.printf(" RMQ-003 comparison ratio slow=%d fast=%d ratio=%.1fx%n",
|
||
sOps, fOps, ratio);
|
||
assert sOps > fOps * 5 : "RMQ-003: expected sOps > fOps*5, got slow=" + sOps + " fast=" + fOps;
|
||
}
|
||
|
||
/**
|
||
* Test 6: RMQ-003 — scaling: doubling Q doubles defective cost, fixed cost constant.
|
||
*/
|
||
static void test_rmq003_scaling_with_queue_type_args() {
|
||
final int D = 21;
|
||
List<String> declareArgs = new ArrayList<>();
|
||
for (int i = 0; i < D; i++) declareArgs.add("x-arg-" + i);
|
||
|
||
List<String> qaLo = new ArrayList<>(), qaHi = new ArrayList<>();
|
||
for (int i = 0; i < 8; i++) qaLo.add("x-qt-" + i);
|
||
for (int i = 0; i < 80; i++) qaHi.add("x-qt-" + i);
|
||
|
||
long sLo = rmq003Defective(declareArgs, qaLo);
|
||
long sHi = rmq003Defective(declareArgs, qaHi);
|
||
long fLo = rmq003Fixed(declareArgs, qaLo);
|
||
long fHi = rmq003Fixed(declareArgs, qaHi);
|
||
|
||
double sScale = (double) sHi / sLo;
|
||
double fScale = (double) fHi / fLo;
|
||
System.out.printf(" RMQ-003 Q scaling slowScale=%.1fx fastScale=%.1fx%n", sScale, fScale);
|
||
assert sScale > 5.0 : "RMQ-003: defective should scale with Q, got " + sScale;
|
||
assert fScale < 5.0 : "RMQ-003: fixed should not scale strongly with Q, got " + fScale;
|
||
}
|
||
|
||
/**
|
||
* Test 7: RMQ-004 — check_arguments_key member scan.
|
||
* A=21 supplied args, I=15 invalid args, worst-case full scan per arg.
|
||
* Defective: O(A*I) = 315. Fixed: O(I+A) = 36.
|
||
*/
|
||
static void test_rmq004_comparison_ratio() {
|
||
final int A = 21, I = 15;
|
||
List<String> suppliedArgs = new ArrayList<>();
|
||
for (int i = 0; i < A; i++) suppliedArgs.add("x-arg-" + i);
|
||
List<String> invalidArgs = new ArrayList<>();
|
||
// invalidArgs that don't overlap with suppliedArgs → worst-case full scan
|
||
for (int i = 0; i < I; i++) invalidArgs.add("x-inv-" + i);
|
||
|
||
long sOps = rmq004Defective(suppliedArgs, invalidArgs);
|
||
long fOps = rmq004Fixed(suppliedArgs, invalidArgs);
|
||
|
||
double ratio = (double) sOps / fOps;
|
||
System.out.printf(" RMQ-004 comparison ratio slow=%d fast=%d ratio=%.1fx%n",
|
||
sOps, fOps, ratio);
|
||
assert sOps > fOps * 5 : "RMQ-004: expected sOps > fOps*5, got slow=" + sOps + " fast=" + fOps;
|
||
}
|
||
|
||
/**
|
||
* Test 8: RMQ-004 — scaling: doubling I doubles defective, barely changes fixed.
|
||
*/
|
||
static void test_rmq004_scaling_with_invalid_args() {
|
||
final int A = 21;
|
||
List<String> suppliedArgs = new ArrayList<>();
|
||
for (int i = 0; i < A; i++) suppliedArgs.add("x-arg-" + i);
|
||
|
||
List<String> invLo = new ArrayList<>(), invHi = new ArrayList<>();
|
||
for (int i = 0; i < 15; i++) invLo.add("x-inv-" + i);
|
||
for (int i = 0; i < 150; i++) invHi.add("x-inv-" + i);
|
||
|
||
long sLo = rmq004Defective(suppliedArgs, invLo);
|
||
long sHi = rmq004Defective(suppliedArgs, invHi);
|
||
long fLo = rmq004Fixed(suppliedArgs, invLo);
|
||
long fHi = rmq004Fixed(suppliedArgs, invHi);
|
||
|
||
double sScale = (double) sHi / sLo;
|
||
double fScale = (double) fHi / fLo;
|
||
System.out.printf(" RMQ-004 I scaling slowScale=%.1fx fastScale=%.1fx%n", sScale, fScale);
|
||
assert sScale > 5.0 : "RMQ-004: defective should scale with I, got " + sScale;
|
||
assert fHi < sHi : "RMQ-004: fixed should be cheaper than defective at high I";
|
||
}
|
||
|
||
/**
|
||
* Test 10: RMQ-001 + RMQ-002 combined — end-to-end correctness.
|
||
* Both defective and fixed paths must agree on which messages match.
|
||
*/
|
||
static void test_correctness_both_defects() {
|
||
// RMQ-001 correctness: matched message sets must be identical
|
||
final int M = 100, P = 8, TARGET = 3;
|
||
Map<Integer, List<Integer>> defUnconf = new HashMap<>();
|
||
Map<Integer, Set<Integer>> fixUnconf = new HashMap<>();
|
||
List<Integer> pl = new ArrayList<>();
|
||
Set<Integer> ps = new HashSet<>();
|
||
for (int p = 0; p < P; p++) { pl.add(p); ps.add(p); }
|
||
for (int m = 0; m < M; m++) {
|
||
defUnconf.put(m, new ArrayList<>(pl));
|
||
fixUnconf.put(m, new HashSet<>(ps));
|
||
}
|
||
List<Integer> defMatched = new ArrayList<>(), fixMatched = new ArrayList<>();
|
||
for (Map.Entry<Integer, List<Integer>> e : defUnconf.entrySet())
|
||
if (e.getValue().contains(TARGET)) defMatched.add(e.getKey());
|
||
for (Map.Entry<Integer, Set<Integer>> e : fixUnconf.entrySet())
|
||
if (e.getValue().contains(TARGET)) fixMatched.add(e.getKey());
|
||
Collections.sort(defMatched); Collections.sort(fixMatched);
|
||
assert defMatched.equals(fixMatched)
|
||
: "RMQ-001 correctness: matched sets differ";
|
||
|
||
// RMQ-002 correctness: both paths must agree on live/dead classification
|
||
List<Integer> members = Arrays.asList(1, 2, 3, 4, 5);
|
||
List<Integer> consumers = Arrays.asList(1, 3, 6, 7, 2); // 6,7 absent
|
||
Set<Integer> memberSet = new HashSet<>(members);
|
||
|
||
List<Integer> defDead = new ArrayList<>(), fixDead = new ArrayList<>();
|
||
for (Integer c : consumers) {
|
||
boolean inList = members.contains(c);
|
||
if (!inList) defDead.add(c);
|
||
}
|
||
for (Integer c : consumers) {
|
||
boolean inSet = memberSet.contains(c);
|
||
if (!inSet) fixDead.add(c);
|
||
}
|
||
assert defDead.equals(fixDead)
|
||
: "RMQ-002 correctness: dead-pid sets differ: " + defDead + " vs " + fixDead;
|
||
|
||
System.out.printf(" Correctness: RMQ-001 matched=%d messages, RMQ-002 dead=%d pids — both agree%n",
|
||
defMatched.size(), defDead.size());
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Main
|
||
// -----------------------------------------------------------------------
|
||
|
||
public static void main(String[] args) {
|
||
System.out.println("RabbitMQQueueTest — CWE-407 model tests (RMQ-001..004)");
|
||
System.out.println("=======================================================");
|
||
|
||
run("test_rmq001_comparison_ratio", RabbitMQQueueTest::test_rmq001_comparison_ratio);
|
||
run("test_rmq001_scaling_with_pids", RabbitMQQueueTest::test_rmq001_scaling_with_pids);
|
||
run("test_rmq002_comparison_ratio", RabbitMQQueueTest::test_rmq002_comparison_ratio);
|
||
run("test_rmq002_scaling_with_consumers", RabbitMQQueueTest::test_rmq002_scaling_with_consumers);
|
||
run("test_rmq003_comparison_ratio", RabbitMQQueueTest::test_rmq003_comparison_ratio);
|
||
run("test_rmq003_scaling_with_queue_type_args", RabbitMQQueueTest::test_rmq003_scaling_with_queue_type_args);
|
||
run("test_rmq004_comparison_ratio", RabbitMQQueueTest::test_rmq004_comparison_ratio);
|
||
run("test_rmq004_scaling_with_invalid_args", RabbitMQQueueTest::test_rmq004_scaling_with_invalid_args);
|
||
run("test_correctness_both_defects", RabbitMQQueueTest::test_correctness_both_defects);
|
||
|
||
System.out.println("=======================================================");
|
||
System.out.println("9/9 PASS");
|
||
}
|
||
|
||
@FunctionalInterface interface TestFn { void run() throws Exception; }
|
||
|
||
static void run(String name, TestFn fn) {
|
||
System.out.println(" [RUN] " + name);
|
||
try {
|
||
fn.run();
|
||
System.out.println(" [PASS] " + name);
|
||
} catch (AssertionError e) {
|
||
System.out.println(" [FAIL] " + name + " — " + e.getMessage());
|
||
System.exit(1);
|
||
} catch (Exception e) {
|
||
System.out.println(" [ERR] " + name + " — " + e);
|
||
System.exit(1);
|
||
}
|
||
}
|
||
}
|