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:
parent
a4a1e444e6
commit
0de158cd7a
8 changed files with 253 additions and 0 deletions
38
defects/zabbix-0001/patch/zabbix-0001.patch
Normal file
38
defects/zabbix-0001/patch/zabbix-0001.patch
Normal 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);
|
||||
Binary file not shown.
BIN
defects/zabbix-0001/test/ZabbixServiceProblemTagDedupTest.class
Normal file
BIN
defects/zabbix-0001/test/ZabbixServiceProblemTagDedupTest.class
Normal file
Binary file not shown.
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
29
defects/zabbix-0002/patch/zabbix-0002.patch
Normal file
29
defects/zabbix-0002/patch/zabbix-0002.patch
Normal 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;
|
||||
BIN
defects/zabbix-0002/test/ZabbixDiscovererQueueDedupTest.class
Normal file
BIN
defects/zabbix-0002/test/ZabbixDiscovererQueueDedupTest.class
Normal file
Binary file not shown.
76
defects/zabbix-0002/test/ZabbixDiscovererQueueDedupTest.java
Normal file
76
defects/zabbix-0002/test/ZabbixDiscovererQueueDedupTest.java
Normal 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
20
defects/zenoss/CLEAN.md
Normal 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue