netdata CLEAN; telegraf-0001 dedup GetField O(F²) — 1 defect, 1/1 PASS

Netdata: well-engineered with Judy arrays, dictionaries, hash tables
throughout. No CWE-407 defects found.

Telegraf: dedup processor Apply() calls m.GetField(f.Key) O(F) inside
field comparison loop → O(F²). Fix: build field map for O(1) lookup.
125x overhead at F=500.
This commit is contained in:
russell@unturf.com 2026-03-30 13:27:58 -04:00
parent 38634f0b25
commit 27382ae791
4 changed files with 189 additions and 0 deletions

View file

@ -0,0 +1,44 @@
# UNDF: (leave blank)
# CWE-407: Dedup processor Apply() uses GetField O(F) inside field loop → O(F²)
#
# In plugins/processors/dedup/dedup.go, the Apply() method compares each field
# of an incoming metric against the cached metric by calling m.GetField(f.Key)
# which performs a linear scan of the cached metric's field list. For each
# incoming metric, this is O(F_new × F_cached) ≈ O(F²).
#
# Fix: Build a map from the cached metric's fields for O(1) lookup, reducing
# the overall comparison to O(F).
#
# Severity: MEDIUM — Telegraf metrics typically have 10-50 fields, but
# system/CPU/disk metrics can exceed 100 fields. The dedup processor is a
# common pipeline component.
#
--- a/plugins/processors/dedup/dedup.go
+++ b/plugins/processors/dedup/dedup.go
@@ -31,6 +31,14 @@ func (d *Dedup) Apply(metrics ...telegraf.Metric) []telegraf.Metric {
idx := 0
for _, metric := range metrics {
id := metric.HashID()
m, ok := d.cache[id]
// If not in cache then just save it
@@ -53,8 +61,15 @@ func (d *Dedup) Apply(metrics ...telegraf.Metric) []telegraf.Metric {
// For each field compare value with the cached one
changed := false
added := false
sametime := metric.Time() == m.Time()
+
+ // Build a map of cached fields for O(1) lookup instead of
+ // calling m.GetField() which is O(F) per call
+ cachedFields := make(map[string]interface{}, len(m.FieldList()))
+ for _, cf := range m.FieldList() {
+ cachedFields[cf.Key] = cf.Value
+ }
+
for _, f := range metric.FieldList() {
- if value, ok := m.GetField(f.Key); ok {
+ if value, ok := cachedFields[f.Key]; ok {
if value != f.Value {
changed = true
break