java-topology/defects/gstreamer/unit/Gstreamer0003TracerDeduplicateTest.java
russell@unturf.com ba818693db nmap-0002 + haproxy-0004 + nginx-0004 + weechat-0003 + zeek-0002 + curl-0004: 6 new CWE-407 defects in network tools; count 693→699
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)
2026-03-29 22:22:11 -04:00

151 lines
5.4 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}