java-topology/defects/loki/patch/loki-0001-dag-edge-dedup.patch

55 lines
2.3 KiB
Diff

# UNDF: UNDF-2026-000000821
# UNDF: (leave blank)
# CWE-407: DAG AddEdge/Eliminate slices.Contains on children/parents O(E^2)
# File: pkg/engine/internal/util/dag/dag.go
# Severity: LOW
# Ratio: ~250x at E=500 edges per node (synthetic; practical fan-out is small)
#
# Graph.AddEdge checks slices.Contains(g.children[e.Parent], e.Child)
# and slices.Contains(g.parents[e.Child], e.Parent) to enforce edge
# uniqueness. If a node accumulates E children, each new AddEdge scans
# the entire children slice, making bulk insertion O(E^2).
#
# Graph.Eliminate has nested loops: for each parent, for each child of
# the eliminated node, slices.Contains(g.children[parent], child)
# scans the parent's children list. Similarly for the parents list.
#
# In Loki's query planner, node fan-out is typically 1-3, so this is
# LOW severity in practice. The fix converts children/parents to
# map[NodeType]struct{} adjacency sets for O(1) membership.
#
# Note: changing []NodeType to map[NodeType]struct{} changes iteration
# order. The current code preserves insertion order via slices. A
# minimal fix would keep the slice for ordering but add a parallel set
# for membership checks.
--- a/pkg/engine/internal/util/dag/dag.go
+++ b/pkg/engine/internal/util/dag/dag.go
@@ -29,8 +29,10 @@ type Graph[NodeType Node] struct {
nodes nodeSet[NodeType]
- parents map[NodeType][]NodeType
- children map[NodeType][]NodeType
+ parents map[NodeType][]NodeType
+ children map[NodeType][]NodeType
+ parentSet map[NodeType]map[NodeType]struct{}
+ childrenSet map[NodeType]map[NodeType]struct{}
}
@@ -95,10 +97,16 @@ func (g *Graph[NodeType]) AddEdge(e Edge[NodeType]) error {
// Uniquely add the edges.
- if !slices.Contains(g.children[e.Parent], e.Child) {
+ if g.childrenSet[e.Parent] == nil {
+ g.childrenSet[e.Parent] = make(map[NodeType]struct{})
+ }
+ if _, exists := g.childrenSet[e.Parent][e.Child]; !exists {
+ g.childrenSet[e.Parent][e.Child] = struct{}{}
g.children[e.Parent] = append(g.children[e.Parent], e.Child)
}
- if !slices.Contains(g.parents[e.Child], e.Parent) {
+ if g.parentSet[e.Child] == nil {
+ g.parentSet[e.Child] = make(map[NodeType]struct{})
+ }
+ if _, exists := g.parentSet[e.Child][e.Parent]; !exists {
+ g.parentSet[e.Child][e.Parent] = struct{}{}
g.parents[e.Child] = append(g.parents[e.Child], e.Parent)
}