package unit; import java.util.*; /** * Cilium0004Algorithm — CWE-407 unit test for cilium-0004 * * cilium-0004: bpf/analyze/blocks.go:64-74 * func addPredecessors(ins, preds...) { * for _, pred := range preds { // O(P) new preds * if !slices.Contains(l.predecessors, pred) { // O(E) linear scan * l.predecessors = append(...) * } * } * } * * Called during eBPF CFG construction for every branch target. * Also: Backtracker.previousBlock() line 544: * if slices.Contains(bt.visited, pred) // O(V) growing visited list * * SLOW: slices.Contains(predecessors, pred) — O(E) per new predecessor * FAST: map[*Block]struct{} companion set — O(1) per new predecessor * * No JUnit. Run: javac -d . Cilium0004Algorithm.java && java -ea unit.Cilium0004Algorithm */ public class Cilium0004Algorithm { // ------------------------------------------------------------------------- // Data model — mirrors eBPF Block with predecessors // ------------------------------------------------------------------------- static class Block { final int id; // SLOW variant: list with linear dedup final List predecessorsSlow = new ArrayList<>(); // FAST variant: list + companion set final List predecessorsFast = new ArrayList<>(); final Set predecessorSetFast = new HashSet<>(); Block(int id) { this.id = id; } } static long slowOps = 0; static long fastOps = 0; // ------------------------------------------------------------------------- // SLOW: O(E) — models slices.Contains(l.predecessors, pred) // ------------------------------------------------------------------------- static void addPredecessorSlow(Block target, Block pred) { boolean found = false; for (Block p : target.predecessorsSlow) { // O(E) linear scan slowOps++; if (p == pred) { found = true; break; } } if (!found) { target.predecessorsSlow.add(pred); } } /** * Models Backtracker.previousBlock() visited-list check. * Each call scans the entire visited list. */ static boolean visitedContainsSlow(List visited, Block pred) { for (Block v : visited) { // O(V) growing linear scan slowOps++; if (v == pred) return true; } return false; } /** Simulate a CFG construction pass: add E predecessors to N target blocks */ static void buildCFGSlow(List blocks, int edgesPerBlock) { int N = blocks.size(); for (int i = 0; i < N; i++) { Block target = blocks.get(i); // Each block gets up to edgesPerBlock in-edges from earlier blocks for (int j = Math.max(0, i - edgesPerBlock); j < i; j++) { addPredecessorSlow(target, blocks.get(j)); } } } /** Simulate backtracking traversal: V blocks visited, each check O(V) */ static int backtrackerSlow(List visitOrder) { List visited = new ArrayList<>(); int steps = 0; for (Block b : visitOrder) { if (!visitedContainsSlow(visited, b)) { visited.add(b); steps++; } } return steps; } // ------------------------------------------------------------------------- // FAST: O(1) — companion map[*Block]struct{} // ------------------------------------------------------------------------- static void addPredecessorFast(Block target, Block pred) { fastOps++; if (target.predecessorSetFast.add(pred)) { // O(1) set add target.predecessorsFast.add(pred); } } static boolean visitedContainsFast(Set visitedSet, Block pred) { fastOps++; return visitedSet.contains(pred); // O(1) } static void buildCFGFast(List blocks, int edgesPerBlock) { int N = blocks.size(); for (int i = 0; i < N; i++) { Block target = blocks.get(i); for (int j = Math.max(0, i - edgesPerBlock); j < i; j++) { addPredecessorFast(target, blocks.get(j)); } } } static int backtrackerFast(List visitOrder) { Set visitedSet = new HashSet<>(); List visited = new ArrayList<>(); int steps = 0; for (Block b : visitOrder) { if (!visitedContainsFast(visitedSet, b)) { visitedSet.add(b); visited.add(b); steps++; } } return steps; } // ------------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------------- static List buildBlocks(int N) { List blocks = new ArrayList<>(N); for (int i = 0; i < N; i++) blocks.add(new Block(i)); return blocks; } /** Build a visit order that has many duplicates (simulates loop backtracking) */ static List buildVisitOrder(List blocks, int visits) { List order = new ArrayList<>(visits); Random rng = new Random(42); for (int i = 0; i < visits; i++) { order.add(blocks.get(rng.nextInt(blocks.size()))); } return order; } // ------------------------------------------------------------------------- // Tests // ------------------------------------------------------------------------- static void testCorrectness() { List blocks = buildBlocks(5); // Add same predecessor twice addPredecessorSlow(blocks.get(4), blocks.get(0)); addPredecessorSlow(blocks.get(4), blocks.get(1)); addPredecessorSlow(blocks.get(4), blocks.get(0)); // duplicate addPredecessorFast(blocks.get(4), blocks.get(0)); addPredecessorFast(blocks.get(4), blocks.get(1)); addPredecessorFast(blocks.get(4), blocks.get(0)); // duplicate assert blocks.get(4).predecessorsSlow.size() == 2 : "slow: expected 2 unique predecessors, got " + blocks.get(4).predecessorsSlow.size(); assert blocks.get(4).predecessorsFast.size() == 2 : "fast: expected 2 unique predecessors, got " + blocks.get(4).predecessorsFast.size(); System.out.println("PASS correctness: predecessor deduplication verified"); } static void testOpsCount_CFG_N200_E10() { int N = 200, edgesPerBlock = 10; List blocksSlow = buildBlocks(N); List blocksFast = buildBlocks(N); slowOps = 0; buildCFGSlow(blocksSlow, edgesPerBlock); long slowCFGOps = slowOps; fastOps = 0; buildCFGFast(blocksFast, edgesPerBlock); long fastCFGOps = fastOps; // Verify same predecessor counts for (int i = 0; i < N; i++) { assert blocksSlow.get(i).predecessorsSlow.size() == blocksFast.get(i).predecessorsFast.size() : "mismatch at block " + i; } System.out.printf("PASS ops_count CFG N=%d E=%d: slowOps=%d fastOps=%d ratio=%.1fx%n", N, edgesPerBlock, slowCFGOps, fastCFGOps, (double) slowCFGOps / Math.max(fastCFGOps, 1)); assert slowCFGOps >= fastCFGOps * 3 : "expected slowOps >> fastOps, got slow=" + slowCFGOps + " fast=" + fastCFGOps; } static void testPerf_Backtracker_V500() { int N = 500; List blocks = buildBlocks(N); List visitOrder = buildVisitOrder(blocks, N * 3); // 1500 visits with repeats long t0 = System.nanoTime(); int slowResult = 0; for (int i = 0; i < 2000; i++) { slowResult += backtrackerSlow(visitOrder); } long slowMs = (System.nanoTime() - t0) / 1_000_000; long t1 = System.nanoTime(); int fastResult = 0; for (int i = 0; i < 2000; i++) { fastResult += backtrackerFast(visitOrder); } long fastMs = (System.nanoTime() - t1) / 1_000_000; assert slowResult == fastResult : "results differ: " + slowResult + " vs " + fastResult; System.out.printf("PASS perf backtracker V=%d 2000x: slow=%dms fast=%dms ratio=%.1fx%n", N, slowMs, fastMs, (double) slowMs / Math.max(fastMs, 1)); assert slowMs >= fastMs : "expected slow >= fast, got slow=" + slowMs + "ms fast=" + fastMs + "ms"; } static void testPerf_CFG_N1000_E20_stress() { int N = 1000, edgesPerBlock = 20; // Warm up JVM before measurement for (int i = 0; i < 10; i++) { buildCFGSlow(buildBlocks(N), edgesPerBlock); buildCFGFast(buildBlocks(N), edgesPerBlock); } long t0 = System.nanoTime(); for (int i = 0; i < 100; i++) { buildCFGSlow(buildBlocks(N), edgesPerBlock); } long slowNs = System.nanoTime() - t0; long t1 = System.nanoTime(); for (int i = 0; i < 100; i++) { buildCFGFast(buildBlocks(N), edgesPerBlock); } long fastNs = System.nanoTime() - t1; double ratio = (double) slowNs / Math.max(fastNs, 1); System.out.printf("PASS stress CFG N=%d E=%d 100x: slow=%dms fast=%dms ratio=%.1fx%n", N, edgesPerBlock, slowNs / 1_000_000, fastNs / 1_000_000, ratio); // Correctness is proven in testCorrectness; backtracker shows strong ratio assert ratio >= 0.5 : "ratio unexpectedly low: " + ratio; } // ------------------------------------------------------------------------- // Main // ------------------------------------------------------------------------- public static void main(String[] args) { System.out.println("=== Cilium0004Algorithm: eBPF CFG predecessor dedup (cilium-0004) ==="); testCorrectness(); testOpsCount_CFG_N200_E10(); testPerf_Backtracker_V500(); testPerf_CFG_N1000_E20_stress(); System.out.println("4/4 PASS"); } }