whitepaper: 366/178 — wave5 defect tables + PDF rebuild

This commit is contained in:
russell@unturf.com 2026-03-27 15:37:42 -04:00
parent 835ae73b0f
commit a4b0cf4edd
79 changed files with 3829 additions and 17 deletions

View file

@ -0,0 +1,132 @@
package unit;
import java.util.*;
/**
* zeek-0001 — CWE-407: O(n²) rule deduplication in per-packet signature matching
*
* Models Zeek src/RuleMatcher.cc is_member_of():
* Slow: std::ranges::find(matched_rules, idx) — O(R) per call, 6 calls per packet
* Fast: unordered_set<intptr_t>::count(idx) — O(1) per call
*
* As a connection accumulates matched rules over P packets,
* each packet costs O(R_matched) per check. Over P packets with R total rules:
* Slow: O(P * R_matched_avg * 6) = O(P * R) with fully loaded state
* Fast: O(P * 1 * 6) = O(P)
*/
public class ZeekRuleMatcherTest {
// --- SLOW: vector-based matched_rules (defective) ---
static boolean isMemberOfSlow(List<Long> matched, long ruleIdx) {
for (long r : matched) {
if (r == ruleIdx) return true;
}
return false;
}
/**
* Simulate P packets across a connection, each triggering up to batchSize rules.
* Per packet: 6 is_member_of checks per candidate rule.
* Returns total operations (inner loop iterations).
*/
static long simulateSlowConnection(int totalRules, int packetsPerConn, int rulesMatchedPerPacket) {
List<Long> matchedRules = new ArrayList<>();
long ops = 0;
Random rng = new Random(7);
for (int pkt = 0; pkt < packetsPerConn; pkt++) {
// Select rulesMatchedPerPacket candidates to check
for (int i = 0; i < rulesMatchedPerPacket; i++) {
long ruleIdx = rng.nextInt(totalRules);
// 6 is_member_of calls model the 6 call sites in RuleMatcher.cc
for (int call = 0; call < 6; call++) {
for (long r : matchedRules) { // O(R) scan
ops++;
if (r == ruleIdx) break;
}
}
// Add to matched_rules if not already present (ExecRuleActions)
if (!isMemberOfSlow(matchedRules, ruleIdx)) {
matchedRules.add(ruleIdx);
}
}
}
return ops;
}
// --- FAST: hash set matched_rules (fixed) ---
static long simulateFastConnection(int totalRules, int packetsPerConn, int rulesMatchedPerPacket) {
Set<Long> matchedRules = new HashSet<>();
long ops = 0;
Random rng = new Random(7);
for (int pkt = 0; pkt < packetsPerConn; pkt++) {
for (int i = 0; i < rulesMatchedPerPacket; i++) {
long ruleIdx = rng.nextInt(totalRules);
// 6 O(1) hash lookups
for (int call = 0; call < 6; call++) {
ops++; // single hash lookup
matchedRules.contains(ruleIdx);
}
matchedRules.add(ruleIdx);
}
}
return ops;
}
public static void main(String[] args) {
// R = total rules in IDS ruleset (large enterprise deployment)
// P = packets per connection
// B = rules fired per packet (typically low, but state accumulates)
final int R = 500;
final int P = 1000;
final int B = 5; // rules that trigger per packet (accumulates in matched_rules)
// Correctness: same rule indices should be found/not-found by both
List<Long> slowList = new ArrayList<>(Arrays.asList(10L, 20L, 30L, 50L, 99L));
Set<Long> fastSet = new HashSet<>(Arrays.asList(10L, 20L, 30L, 50L, 99L));
assert isMemberOfSlow(slowList, 30L) : "slow: should find 30";
assert !isMemberOfSlow(slowList, 40L) : "slow: should miss 40";
assert fastSet.contains(30L) : "fast: should find 30";
assert !fastSet.contains(40L) : "fast: should miss 40";
long slowOps = simulateSlowConnection(R, P, B);
long fastOps = simulateFastConnection(R, P, B);
assert slowOps > fastOps : "slow should cost more ops, got slow=" + slowOps + " fast=" + fastOps;
double speedup = (double) slowOps / fastOps;
System.out.println("zeek-0001 CWE-407: is_member_of O(R) vector scan vs O(1) hash set");
System.out.println(" R (rules) = " + R + ", P (packets/conn) = " + P + ", B (rules/pkt) = " + B);
System.out.println(" Slow ops (vector linear scan, 6x per pkt): " + slowOps);
System.out.println(" Fast ops (hash set, 6x per pkt): " + fastOps);
System.out.printf (" Speedup: %.0fx%n", speedup);
System.out.println();
// Verify the quadratic growth: at 2× rules the slow cost should ~4× (quadratic)
long slowOpsHalfR = simulateSlowConnection(R/2, P, B);
long slowOpsDoubleR= simulateSlowConnection(R*2, P, B);
double ratio = (double) slowOpsDoubleR / slowOpsHalfR;
// For a truly quadratic structure ratio approaches 4.0; assert it's super-linear
assert ratio > 2.0 : "expected super-linear growth, got ratio=" + ratio;
long fastOpsHalfR = simulateFastConnection(R/2, P, B);
long fastOpsDoubleR = simulateFastConnection(R*2, P, B);
double fastRatio = (double) fastOpsDoubleR / fastOpsHalfR;
// Fast should be roughly linear (ratio ~2) or even flat (constant O(1) per call)
assert fastRatio < ratio : "fast should scale better than slow";
System.out.println("Quadratic growth check (R/2 → 2R):");
System.out.println(" Slow ops ratio: " + String.format("%.2f", ratio) + "x (>2 confirms super-linear)");
System.out.println(" Fast ops ratio: " + String.format("%.2f", fastRatio) + "x (near-linear)");
System.out.println();
System.out.println("2/2 PASS");
}
}