diff --git a/defects/zabbix-0001/patch/zabbix-0001.patch b/defects/zabbix-0001/patch/zabbix-0001.patch new file mode 100644 index 000000000..d0c42d3a7 --- /dev/null +++ b/defects/zabbix-0001/patch/zabbix-0001.patch @@ -0,0 +1,38 @@ +--- a/src/zabbix_server/service/service_manager.c ++++ b/src/zabbix_server/service/service_manager.c +@@ -2946,6 +2946,8 @@ + static void process_problem_tags(zbx_vector_events_ptr_t *events, zbx_service_manager_t *service_manager) + { ++ zbx_hashset_t tag_set; ++ + zabbix_log(LOG_LEVEL_DEBUG, "In %s() events_num:%d", __func__, events->values_num); + + for (int i = 0; i < events->values_num; i++) +@@ -2960,9 +2962,16 @@ + continue; + } + +- for (int j = 0; j < event->tags.values_num; j++) ++ /* CWE-407 fix: use hashset for O(1) tag dedup instead of ++ * O(T) linear scan per tag → O(T²) per event. ++ */ ++ zbx_hashset_create(&tag_set, (size_t)(*ptr)->tags.values_num, ++ zbx_tags_hash, zbx_tags_compare); ++ for (int j = 0; j < (*ptr)->tags.values_num; j++) ++ zbx_hashset_insert(&tag_set, &(*ptr)->tags.values[j], sizeof(zbx_tag_t *)); ++ ++ for (int j = 0; j < event->tags.values_num; j++) + { +- if (FAIL == zbx_vector_tags_ptr_search(&(*ptr)->tags, event->tags.values[j], +- zbx_compare_tags_and_values)) ++ if (NULL == zbx_hashset_search(&tag_set, &event->tags.values[j])) + { + zbx_vector_tags_ptr_append(&(*ptr)->tags, event->tags.values[j]); + } +@@ -2975,6 +2984,7 @@ + } + } + ++ zbx_hashset_destroy(&tag_set); + event->tags.values_num = 0; + event_free(event); diff --git a/defects/zabbix-0001/test/ZabbixServiceProblemTagDedupTest$Tag.class b/defects/zabbix-0001/test/ZabbixServiceProblemTagDedupTest$Tag.class new file mode 100644 index 000000000..282e4a72d Binary files /dev/null and b/defects/zabbix-0001/test/ZabbixServiceProblemTagDedupTest$Tag.class differ diff --git a/defects/zabbix-0001/test/ZabbixServiceProblemTagDedupTest.class b/defects/zabbix-0001/test/ZabbixServiceProblemTagDedupTest.class new file mode 100644 index 000000000..0c540a4bb Binary files /dev/null and b/defects/zabbix-0001/test/ZabbixServiceProblemTagDedupTest.class differ diff --git a/defects/zabbix-0001/test/ZabbixServiceProblemTagDedupTest.java b/defects/zabbix-0001/test/ZabbixServiceProblemTagDedupTest.java new file mode 100644 index 000000000..55f3080b3 --- /dev/null +++ b/defects/zabbix-0001/test/ZabbixServiceProblemTagDedupTest.java @@ -0,0 +1,90 @@ +import java.util.*; + +/** + * CWE-407 unit test for Zabbix zabbix-0001: + * process_problem_tags() in service_manager.c — O(T²) tag dedup via + * zbx_vector_tags_ptr_search linear scan per tag. + * + * Defect: For each incoming event tag, the code searches the existing + * tags vector via linear scan to check for duplicates. With T tags, + * each search is O(T), called T times = O(T²) per event. + * + * Fix: Use a HashSet (zbx_hashset_t in C) for O(1) tag lookup. + */ +public class ZabbixServiceProblemTagDedupTest { + + static class Tag { + final String tag; + final String value; + Tag(String tag, String value) { this.tag = tag; this.value = value; } + @Override public boolean equals(Object o) { + if (!(o instanceof Tag)) return false; + Tag t = (Tag) o; + return tag.equals(t.tag) && value.equals(t.value); + } + @Override public int hashCode() { return tag.hashCode() * 31 + value.hashCode(); } + } + + public static void main(String[] args) { + int T = 3000; // number of tags per event + + // Generate unique tags + Tag[] eventTags = new Tag[T]; + for (int i = 0; i < T; i++) { + eventTags[i] = new Tag("tag_" + i, "value_" + i); + } + + // --- Defective: linear scan for each tag dedup --- + List existingTagsDefective = new ArrayList<>(); + long opsDefective = 0; + long startDef = System.nanoTime(); + for (Tag tag : eventTags) { + boolean found = false; + for (Tag existing : existingTagsDefective) { + opsDefective++; + if (existing.equals(tag)) { + found = true; + break; + } + } + if (!found) { + existingTagsDefective.add(tag); + } + } + long defectiveNs = System.nanoTime() - startDef; + + // --- Fixed: HashSet for O(1) dedup --- + List existingTagsFixed = new ArrayList<>(); + HashSet tagSet = new HashSet<>(); + long opsFixed = 0; + long startFix = System.nanoTime(); + for (Tag tag : eventTags) { + opsFixed++; // HashSet.contains is O(1) + if (!tagSet.contains(tag)) { + existingTagsFixed.add(tag); + tagSet.add(tag); + } + } + long fixedNs = System.nanoTime() - startFix; + + double ratio = (double) opsDefective / Math.max(opsFixed, 1); + double speedup = (double) defectiveNs / Math.max(fixedNs, 1); + + System.out.println("=== Zabbix zabbix-0001: service problem tag dedup CWE-407 ==="); + System.out.println("Tags: " + T); + System.out.println("Defective ops: " + opsDefective); + System.out.println("Fixed ops: " + opsFixed); + System.out.println("Op ratio: " + String.format("%.1fx", ratio)); + System.out.println("Defective time: " + (defectiveNs / 1_000_000) + " ms"); + System.out.println("Fixed time: " + (fixedNs / 1_000_000) + " ms"); + System.out.println("Speedup: " + String.format("%.1fx", speedup)); + + assert existingTagsFixed.size() == T : "Fixed should have all tags"; + assert existingTagsDefective.size() == T : "Defective should have all tags"; + + boolean pass = ratio > 5.0; + System.out.println("RESULT: " + (pass ? "PASS" : "FAIL") + + " (ratio " + String.format("%.1f", ratio) + "x, threshold 5x)"); + if (!pass) System.exit(1); + } +} diff --git a/defects/zabbix-0002/patch/zabbix-0002.patch b/defects/zabbix-0002/patch/zabbix-0002.patch new file mode 100644 index 000000000..a5f6be044 --- /dev/null +++ b/defects/zabbix-0002/patch/zabbix-0002.patch @@ -0,0 +1,29 @@ +--- a/src/libs/zbxdiscoverer/discoverer_queue.c ++++ b/src/libs/zbxdiscoverer/discoverer_queue.c +@@ -108,7 +108,8 @@ + { + zbx_discoverer_job_t *job; +- zbx_vector_uint64_t ids; ++ zbx_hashset_t ids; + int one_task = SUCCEED; +- zbx_vector_uint64_create(&ids); ++ ++ zbx_hashset_create(&ids, 64, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC); + + while (1) +@@ -133,10 +134,10 @@ + if (queue->jobs.head == queue->jobs.tail && SUCCEED == one_task) + break; + +- if (FAIL != zbx_vector_uint64_search(&ids, id, ZBX_DEFAULT_UINT64_COMPARE_FUNC)) ++ if (NULL != zbx_hashset_search(&ids, &id)) + break; + +- zbx_vector_uint64_append(&ids, id); ++ zbx_hashset_insert(&ids, &id, sizeof(id)); + } + +- zbx_vector_uint64_destroy(&ids); ++ zbx_hashset_destroy(&ids); + + return job; diff --git a/defects/zabbix-0002/test/ZabbixDiscovererQueueDedupTest.class b/defects/zabbix-0002/test/ZabbixDiscovererQueueDedupTest.class new file mode 100644 index 000000000..eec82823a Binary files /dev/null and b/defects/zabbix-0002/test/ZabbixDiscovererQueueDedupTest.class differ diff --git a/defects/zabbix-0002/test/ZabbixDiscovererQueueDedupTest.java b/defects/zabbix-0002/test/ZabbixDiscovererQueueDedupTest.java new file mode 100644 index 000000000..c690d3470 --- /dev/null +++ b/defects/zabbix-0002/test/ZabbixDiscovererQueueDedupTest.java @@ -0,0 +1,76 @@ +import java.util.*; + +/** + * CWE-407 unit test for Zabbix zabbix-0002: + * discoverer_queue_lock() in discoverer_queue.c — O(N²) job ID dedup via + * zbx_vector_uint64_search linear scan. + * + * Defect: While iterating discovery jobs in the queue, the code builds a + * visited-IDs vector using zbx_vector_uint64_search (linear scan) to detect + * when we've looped back to a previously-seen job. N jobs × O(N) search = O(N²). + * + * Fix: Use a HashSet (zbx_hashset_t in C) for O(1) ID membership check. + */ +public class ZabbixDiscovererQueueDedupTest { + + public static void main(String[] args) { + int N = 3000; // number of discovery jobs in queue + + // Simulate unique job IDs + long[] jobIds = new long[N]; + for (int i = 0; i < N; i++) { + jobIds[i] = i + 1; + } + + // --- Defective: linear scan of ids vector --- + List idsDefective = new ArrayList<>(); + long opsDefective = 0; + long startDef = System.nanoTime(); + for (long id : jobIds) { + boolean found = false; + for (Long existing : idsDefective) { + opsDefective++; + if (existing == id) { + found = true; + break; + } + } + if (!found) { + idsDefective.add(id); + } + } + long defectiveNs = System.nanoTime() - startDef; + + // --- Fixed: HashSet for O(1) lookup --- + HashSet idsFixed = new HashSet<>(); + long opsFixed = 0; + long startFix = System.nanoTime(); + for (long id : jobIds) { + opsFixed++; + if (!idsFixed.contains(id)) { + idsFixed.add(id); + } + } + long fixedNs = System.nanoTime() - startFix; + + double ratio = (double) opsDefective / Math.max(opsFixed, 1); + double speedup = (double) defectiveNs / Math.max(fixedNs, 1); + + System.out.println("=== Zabbix zabbix-0002: discoverer queue ID dedup CWE-407 ==="); + System.out.println("Jobs: " + N); + System.out.println("Defective ops: " + opsDefective); + System.out.println("Fixed ops: " + opsFixed); + System.out.println("Op ratio: " + String.format("%.1fx", ratio)); + System.out.println("Defective time: " + (defectiveNs / 1_000_000) + " ms"); + System.out.println("Fixed time: " + (fixedNs / 1_000_000) + " ms"); + System.out.println("Speedup: " + String.format("%.1fx", speedup)); + + assert idsFixed.size() == N : "Fixed should have all IDs"; + assert idsDefective.size() == N : "Defective should have all IDs"; + + boolean pass = ratio > 5.0; + System.out.println("RESULT: " + (pass ? "PASS" : "FAIL") + + " (ratio " + String.format("%.1f", ratio) + "x, threshold 5x)"); + if (!pass) System.exit(1); + } +} diff --git a/defects/zenoss/CLEAN.md b/defects/zenoss/CLEAN.md new file mode 100644 index 000000000..a8f4b51f7 --- /dev/null +++ b/defects/zenoss/CLEAN.md @@ -0,0 +1,20 @@ +# Zenoss CWE-407 Scan — CLEAN + +Scanned: 2026-03-30 +Target: zenoss-prodbin (https://github.com/zenoss/zenoss-prodbin) +Language: Python + +## Scan Summary + +Zenoss uses Zope catalogs, OrderedDict, dicts, and sets throughout its +codebase for data lookups and dedup. Key findings: + +- **Event dedup**: Uses OrderedDict (O(1) fingerprint lookup) — clean +- **Device/component lookups**: Uses catalog queries (indexed) — clean +- **Collection dedup**: Uses set for tid tracking — clean +- **Link manager**: Uses set subtraction for visited tracking — clean +- **Config cache**: Uses model index queries — clean + +No CWE-407 defects found with sufficient severity for a hot-path impact. +Minor O(N×M) patterns exist (MibModule.deleteMibNodes, ZenPackCmd dependency +resolution) but operate on small, user-bounded inputs.