nmap-0002: nmap.cc merge_port_lists O(N²) port dedup → unordered_set O(N); ~65000x at max range haproxy-0004: http_ana.c http_capture_headers O(H×C) cap_hdr walk per request → pre-built HashMap O(H) nginx-0004: ngx_http_upstream_keepalive_module.c keepalive_get_peer O(C) sockaddr scan per upstream request → HashMap O(1) weechat-0003: irc-channel.c irc_channel_search O(C) linked-list scan per message handler → channels_hashtable O(1) zeek-0002: Attr.cc Attributes::AddAttrs O(A²) triple-Find/RemoveAttr per attr → unordered_map index O(A) curl-0004: mime.c search_header O(P×H) 3x per part per mime_add_headers → pre-indexed header name set O(P)
151 lines
5.4 KiB
Java
151 lines
5.4 KiB
Java
package unit;
|
||
|
||
import java.util.ArrayList;
|
||
import java.util.HashSet;
|
||
import java.util.List;
|
||
import java.util.Set;
|
||
|
||
/**
|
||
* Gstreamer0003TracerDeduplicateTest — CWE-407 gstreamer-0003
|
||
*
|
||
* Models gst_tracing_get_active_tracers() in gst/gsttracerutils.c:
|
||
* slow() = O(H × T²) via list-indexOf dedup (current defect)
|
||
* fast() = O(H × T) via HashSet dedup (patch)
|
||
*
|
||
* Parameters: H hook types, T tracers registered per hook (with overlap).
|
||
* Assert: slowOps > fastOps * Nx at H=54 hooks, T=5 tracers each covering 10 hooks.
|
||
*/
|
||
public class Gstreamer0003TracerDeduplicateTest {
|
||
|
||
static long slowOps;
|
||
static long fastOps;
|
||
|
||
/**
|
||
* Simulates the tracer seen-list: a List<Integer> (tracer ids).
|
||
* g_list_index equivalent = list.indexOf() = O(n).
|
||
*/
|
||
static int listIndexOf(List<Integer> list, int val) {
|
||
for (int i = 0; i < list.size(); i++) {
|
||
slowOps++;
|
||
if (list.get(i) == val) return i;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/**
|
||
* slow: O(H × T²) — models original gst_tracing_get_active_tracers.
|
||
* For each hook bucket, for each tracer-hook entry, calls listIndexOf.
|
||
*/
|
||
static List<Integer> getActiveTracersSlow(int[][] hookBuckets) {
|
||
List<Integer> tracers = new ArrayList<>();
|
||
for (int[] bucket : hookBuckets) {
|
||
for (int tracerId : bucket) {
|
||
if (listIndexOf(tracers, tracerId) < 0) {
|
||
tracers.add(tracerId);
|
||
}
|
||
}
|
||
}
|
||
return tracers;
|
||
}
|
||
|
||
/**
|
||
* fast: O(H × T) — models HashSet fix.
|
||
* Uses O(1) set membership test.
|
||
*/
|
||
static List<Integer> getActiveTracersFast(int[][] hookBuckets) {
|
||
List<Integer> tracers = new ArrayList<>();
|
||
Set<Integer> seen = new HashSet<>();
|
||
for (int[] bucket : hookBuckets) {
|
||
for (int tracerId : bucket) {
|
||
fastOps++;
|
||
if (!seen.contains(tracerId)) {
|
||
seen.add(tracerId);
|
||
tracers.add(tracerId);
|
||
}
|
||
}
|
||
}
|
||
return tracers;
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
final int NX = 5;
|
||
|
||
// H=54 hook types; T=5 tracers, each registered under ~10 of 54 hooks
|
||
final int H = 54;
|
||
final int T = 5; // distinct tracers
|
||
final int HOOKS_PER_TRACER = 10;
|
||
|
||
// Build hookBuckets: H buckets, each containing tracers registered for that hook
|
||
int[][] hookBuckets = new int[H][];
|
||
int[] hookCounts = new int[H];
|
||
// Assign each tracer to HOOKS_PER_TRACER hooks
|
||
int[][] tracerHooks = new int[T][];
|
||
for (int t = 0; t < T; t++) {
|
||
tracerHooks[t] = new int[HOOKS_PER_TRACER];
|
||
for (int h = 0; h < HOOKS_PER_TRACER; h++) {
|
||
tracerHooks[t][h] = (t * HOOKS_PER_TRACER + h) % H;
|
||
}
|
||
}
|
||
// Count tracer registrations per hook
|
||
int[] buckSize = new int[H];
|
||
for (int t = 0; t < T; t++) {
|
||
for (int h : tracerHooks[t]) buckSize[h]++;
|
||
}
|
||
hookBuckets = new int[H][];
|
||
for (int h = 0; h < H; h++) hookBuckets[h] = new int[buckSize[h]];
|
||
int[] buckIdx = new int[H];
|
||
for (int t = 0; t < T; t++) {
|
||
for (int h : tracerHooks[t]) {
|
||
hookBuckets[h][buckIdx[h]++] = t;
|
||
}
|
||
}
|
||
|
||
// Warm up
|
||
slowOps = 0; fastOps = 0;
|
||
getActiveTracersSlow(hookBuckets);
|
||
getActiveTracersFast(hookBuckets);
|
||
|
||
// Measure
|
||
slowOps = 0; fastOps = 0;
|
||
final int CALLS = 1000;
|
||
List<Integer> slowResult = null, fastResult = null;
|
||
for (int c = 0; c < CALLS; c++) {
|
||
slowResult = getActiveTracersSlow(hookBuckets);
|
||
fastResult = getActiveTracersFast(hookBuckets);
|
||
}
|
||
|
||
// Correctness: both should find T unique tracers
|
||
boolean correctnessOk = (slowResult != null && fastResult != null &&
|
||
slowResult.size() == T && fastResult.size() == T);
|
||
boolean speedupOk = slowOps > fastOps * NX;
|
||
|
||
System.out.printf("H=%d hooks, T=%d tracers, %d hooks/tracer, CALLS=%d%n",
|
||
H, T, HOOKS_PER_TRACER, CALLS);
|
||
System.out.printf("slow (list-indexOf) ops: %d%n", slowOps);
|
||
System.out.printf("fast (HashSet) ops: %d%n", fastOps);
|
||
System.out.printf("speedup ratio: %.1fx (required >%dx)%n",
|
||
(double) slowOps / fastOps, NX);
|
||
System.out.printf("tracers found: slow=%d fast=%d (expected %d)%n",
|
||
slowResult == null ? -1 : slowResult.size(),
|
||
fastResult == null ? -1 : fastResult.size(), T);
|
||
|
||
int passed = 0, total = 2;
|
||
if (correctnessOk) {
|
||
System.out.printf("1/2 PASS correctness: both found %d tracers%n", T);
|
||
passed++;
|
||
} else {
|
||
System.out.printf("1/2 FAIL correctness: slow=%d fast=%d expected=%d%n",
|
||
slowResult == null ? -1 : slowResult.size(),
|
||
fastResult == null ? -1 : fastResult.size(), T);
|
||
}
|
||
if (speedupOk) {
|
||
System.out.printf("2/2 PASS speedup: %d > %d * %d%n", slowOps, fastOps, NX);
|
||
passed++;
|
||
} else {
|
||
System.out.printf("2/2 FAIL speedup: %d not > %d * %d%n", slowOps, fastOps, NX);
|
||
}
|
||
|
||
System.out.printf("%d/%d PASS%n", passed, total);
|
||
if (passed < total) System.exit(1);
|
||
}
|
||
}
|