2.8 KiB
2.8 KiB
UNDF: UNDF-2026-000000367
UNDF: (pending)
consul-0001: makeMeshGatewayPeerFilterChain — peerNames O(S×B×P) slice lookup
CWE-407 — Algorithmic Complexity
| Field | Value |
|---|---|
| ID | consul-0001 |
| Severity | MEDIUM |
| Ecosystem | consul |
| Package | agent/xds |
| File | agent/xds/listeners.go |
| Lines | 2282–2286 |
| Complexity | O(S×B×P) — services × bundles × peers per service |
| Hot path | Mesh gateway listener rebuild on every topology change |
Defect
makeMeshGatewayPeerFilterChain receives peerNames []string (the set of peer names
for this exported service) and iterates over all peering trust bundles, calling
stringslice.Contains(peerNames, bundle.PeerName) for each bundle:
// agent/xds/listeners.go
for _, bundle := range cfgSnap.MeshGateway.PeeringTrustBundles { // O(B)
if stringslice.Contains(peerNames, bundle.PeerName) { // O(P) scan
peerBundles = append(peerBundles, bundle)
}
}
stringslice.Contains is a linear scan:
// lib/stringslice/stringslice.go
func Contains(l []string, s string) bool {
for _, v := range l {
if v == s {
return true
}
}
return false
}
The outer caller iterates over all exported services:
for _, svc := range cfgSnap.MeshGatewayValidExportedServices() { // O(S)
peerNames := cfgSnap.MeshGateway.ExportedServicesWithPeers[svc]
filterChain, err := s.makeMeshGatewayPeerFilterChain(cfgSnap, svc, peerNames, chain)
Total: O(S × B × P). In a production Consul cluster with S=50 services, B=20 trust bundles, P=20 peers per service, each listener rebuild costs 50×20×20 = 20,000 string comparisons instead of 50×20 = 1,000 map lookups.
Fix
Convert peerNames to a map[string]bool before the bundle loop:
// Before:
var peerBundles []*pbpeering.PeeringTrustBundle
for _, bundle := range cfgSnap.MeshGateway.PeeringTrustBundles {
if stringslice.Contains(peerNames, bundle.PeerName) {
peerBundles = append(peerBundles, bundle)
}
}
// After:
peerNameSet := make(map[string]struct{}, len(peerNames))
for _, p := range peerNames {
peerNameSet[p] = struct{}{}
}
var peerBundles []*pbpeering.PeeringTrustBundle
for _, bundle := range cfgSnap.MeshGateway.PeeringTrustBundles {
if _, ok := peerNameSet[bundle.PeerName]; ok {
peerBundles = append(peerBundles, bundle)
}
}
Alternatively, ExportedServicesWithPeers could store map[string]struct{} instead of
[]string, propagating O(1) lookup to all callers.
Speedup
| S (services) | B (bundles) | P (peers) | Before (ops) | After (ops) | Speedup |
|---|---|---|---|---|---|
| 10 | 10 | 10 | 1,000 | 200 | 5× |
| 50 | 20 | 20 | 20,000 | 2,000 | 10× |
| 100 | 50 | 50 | 250,000 | 10,000 | 25× |