monitoring scan: nagioscore 2 defects, zabbix 2 defects, zenoss CLEAN

nagioscore-0001: add_notification find_notification linked-list O(C²) HIGH 999x
nagioscore-0002: add_object_to_objectlist linked-list dedup O(N²) MEDIUM 1499x
zabbix-0001: process_problem_tags tag dedup O(T²) HIGH 1499x
zabbix-0002: discoverer_queue_lock job ID dedup O(N²) MEDIUM 1499x
zenoss: CLEAN — uses OrderedDict/set/catalog throughout

4/4 PASS, 4 defects across 2 targets
This commit is contained in:
russell@unturf.com 2026-03-30 16:56:56 -04:00
parent a4a1e444e6
commit 0de158cd7a
8 changed files with 253 additions and 0 deletions

View file

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

View file

@ -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<Tag> 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<Tag> existingTagsFixed = new ArrayList<>();
HashSet<Tag> 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);
}
}

View file

@ -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;

View file

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

20
defects/zenoss/CLEAN.md Normal file
View file

@ -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.