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)
This commit is contained in:
russell@unturf.com 2026-03-29 22:22:11 -04:00
parent 421f3352c7
commit ba818693db
21 changed files with 2213 additions and 0 deletions

View file

@ -0,0 +1,48 @@
--- a/subprojects/gstreamer/plugins/elements/gstinputselector.c
+++ b/subprojects/gstreamer/plugins/elements/gstinputselector.c
@@ -1806,7 +1806,8 @@ gst_input_selector_event (GstPad * pad, GstObject * parent, GstEvent * event)
gboolean result = FALSE;
GstIterator *iter;
gboolean done = FALSE;
GValue item = { 0, };
GstPad *eventpad;
- GList *pushed_pads = NULL;
+ /* CWE-407 fix: replace O(N) g_list_find seen-list with O(1) hash set */
+ GHashTable *pushed_pads_set = g_hash_table_new (g_direct_hash, g_direct_equal);
sel = GST_INPUT_SELECTOR (parent);
iter = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (sel));
@@ -1826,7 +1827,7 @@ gst_input_selector_event (GstPad * pad, GstObject * parent, GstEvent * event)
gst_event_ref (event);
result |= gst_pad_push_event (eventpad, event);
- pushed_pads = g_list_append (pushed_pads, eventpad);
+ g_hash_table_add (pushed_pads_set, eventpad);
gst_object_unref (eventpad);
} else {
GST_INPUT_SELECTOR_UNLOCK (sel);
@@ -1838,19 +1839,19 @@ gst_input_selector_event (GstPad * pad, GstObject * parent, GstEvent * event)
eventpad = g_value_get_object (&item);
- /* if already pushed, skip */
- if (g_list_find (pushed_pads, eventpad)) {
+ /* CWE-407 fix: O(1) hash lookup replaces O(N) g_list_find scan */
+ if (g_hash_table_contains (pushed_pads_set, eventpad)) {
g_value_reset (&item);
break;
}
gst_event_ref (event);
result |= gst_pad_push_event (eventpad, event);
- pushed_pads = g_list_append (pushed_pads, eventpad);
+ g_hash_table_add (pushed_pads_set, eventpad);
g_value_reset (&item);
break;
@@ -1870,7 +1871,7 @@ gst_input_selector_event (GstPad * pad, GstObject * parent, GstEvent * event)
g_value_unset (&item);
gst_iterator_free (iter);
- g_list_free (pushed_pads);
+ g_hash_table_destroy (pushed_pads_set);
gst_event_unref (event);
return result;

View file

@ -0,0 +1,55 @@
--- a/subprojects/gstreamer/gst/gsttracerutils.c
+++ b/subprojects/gstreamer/gst/gsttracerutils.c
@@ -435,26 +435,45 @@ gst_tracing_get_active_tracers (void)
* Returns: (transfer full) (element-type Gst.Tracer): A #GList of
* #GstTracer objects
*
* Since: 1.18
*/
GList *
gst_tracing_get_active_tracers (void)
{
- GList *tracers, *h_list, *h_node, *t_node;
+ GList *tracers = NULL, *h_list, *h_node, *t_node;
GstTracerHook *hook;
+ /*
+ * CWE-407 fix: replace g_list_index() dedup (O(T) per insertion,
+ * O(H×T²) total) with a GHashTable keyed on GstTracer* for O(1) lookup.
+ *
+ * Original code note says "O(n) but fine since tracers count is small".
+ * However: with H=54 hook types and T tracers per hook the outer loop
+ * iterates H×T times, each calling g_list_index(tracers, …) which is
+ * O(accumulated_tracers). For a pipeline with 5 tracers each covering
+ * ~10 hooks: 54×10 = 540 outer iterations, each scanning up to 5 entries
+ * = 2700 list-index calls. The fix reduces this to 540 hash lookups.
+ *
+ * GHashTable with g_direct_hash/g_direct_equal is appropriate here since
+ * GstTracer* pointers are stable object identities.
+ */
+ GHashTable *seen;
if (!_priv_tracer_enabled || !_priv_tracers)
return NULL;
- tracers = NULL;
+ seen = g_hash_table_new (g_direct_hash, g_direct_equal);
h_list = g_hash_table_get_values (_priv_tracers);
for (h_node = h_list; h_node; h_node = g_list_next (h_node)) {
for (t_node = h_node->data; t_node; t_node = g_list_next (t_node)) {
hook = (GstTracerHook *) t_node->data;
- /* Skip duplicate tracers from different hooks. This function is O(n), but
- * that should be fine since the number of tracers enabled on a process
- * should be small. */
- if (g_list_index (tracers, hook->tracer) >= 0)
+ /* CWE-407 fix: O(1) hash lookup replaces O(T) g_list_index() scan */
+ if (g_hash_table_contains (seen, hook->tracer))
continue;
+ g_hash_table_add (seen, hook->tracer);
tracers = g_list_prepend (tracers, gst_object_ref (hook->tracer));
}
}
g_list_free (h_list);
+ g_hash_table_destroy (seen);
return tracers;
}

View file

@ -0,0 +1,151 @@
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);
}
}