rpcs3 (4, C++), ppsspp (4, C++), spring-framework (3, Java), nats-server (3, Go), minio (3, Go), gimp (3, C), cockroach (3, Go), superset (3, Python). Note: rpcs3-0004 is CWE-312, rest are CWE-407.
5.1 KiB
NATS Server — CWE-407 Disclosure Brief
2026-04-13 · Patches available — awaiting upstream merge
Finding
Three O(n²) defects in NATS Server across JetStream cluster peer filtering, route diff during configuration reload, and consumer subject-filter overlap validation. All patched. Patches ready for upstream review. Two defects fire during cluster reconfiguration; one fires on every consumer create/update/recover.
The Defects
nats-0001 (PATCHED — MEDIUM): server/jetstream_cluster.go:8125
// In processStreamUpdateRequest — fires during stream replica changes:
for _, peer := range rg.Peers {
if !slices.Contains(nrg.Peers, peer) { // O(N) linear scan
peerSet = append(peerSet, peer)
}
}
slices.Contains scans the new peer list for every peer in the old list — O(R×N) where R = old peers, N = new peers. Fires during stream replica reassignment in JetStream clusters.
nats-server-0001 (PATCHED — MEDIUM): server/reload.go:2605
// In diffRoutes — fires during config reload:
removeLoop:
for _, oldRoute := range old {
for _, newRoute := range new { // O(R²) nested loop
if urlsAreEqual(oldRoute, newRoute) {
continue removeLoop
}
}
remove = append(remove, oldRoute)
}
diffRoutes compares old and new route lists with two O(R²) nested loops — one for removals, one for additions. urlsAreEqual does string comparison per pair. At R=100 routes, each reload triggers 20,000 comparisons.
nats-server-0002 (PATCHED — MEDIUM): server/consumer.go:824
// In checkConsumerCfg — fires on every consumer create/update/recover:
for outer, subject := range subjectFilters {
for inner, ssubject := range subjectFilters { // O(S²) nested loop
if inner != outer && subjectIsSubsetMatch(subject, ssubject) {
return NewJSConsumerOverlappingSubjectFiltersError()
}
}
}
checkConsumerCfg validates that no two consumer FilterSubjects entries overlap via a double nested loop calling subjectIsSubsetMatch for every ordered pair — O(S²) where S = filter subjects. At S=256, this runs 65,280 match calls per consumer validation. Fires during consumer creation, update, recovery, and cluster re-sync.
Complexity Proof
nats-0001: At R=50 old peers, N=50 new peers:
- Defective: 50 × 50 = 2,500 comparisons
- Fixed: 50 + 50 = 100 operations (map build + probe)
- 25× op reduction.
nats-server-0001: At R=100 routes:
- Defective: 2 × (100 × 100) = 20,000 comparisons
- Fixed: 2 × 100 = 200 operations (map build + probe)
- 100× op reduction.
nats-server-0002: At S=256 filter subjects:
- Defective: 256 × 255 = 65,280 match calls
- Fixed: 256 × 255 / 2 = 32,640 match calls (triangle loop)
- 2× op reduction. Asymptotic improvement is constant 2x, but eliminates the
inner != outerbranch misprediction in the hot inner loop.
Impact
NATS Server powers messaging infrastructure for thousands of organizations — from edge IoT deployments to large-scale microservice architectures. nats-0001 fires during JetStream stream replica reassignment, a core cluster management operation. nats-server-0001 fires during every configuration reload with route changes — in large mesh clusters with many routes, the O(R²) diff dominates reload latency. nats-server-0002 fires on every consumer create/update/recover path — JetStream deployments with many filtered consumers (hundreds of subject filters per consumer) hit this on every consumer operation.
The Fix
nats-0001: Build a map[string]struct{} from nrg.Peers for O(1) membership test instead of slices.Contains.
// Before
if !slices.Contains(nrg.Peers, peer) { ... }
// After
nrgPeerSet := make(map[string]struct{}, len(nrg.Peers))
for _, p := range nrg.Peers { nrgPeerSet[p] = struct{}{} }
if _, ok := nrgPeerSet[peer]; !ok { ... }
nats-server-0001: Build map[string]struct{} sets from each route list's canonical URL strings. Replace two O(R²) nested loops with two O(R) map probes.
nats-server-0002: Short-circuit the overlap check to iterate only unordered pairs (triangle loop from j = i+1), checking both directions of subjectIsSubsetMatch per pair.
Patch
Fixes available:
defects/nats-server/patch/nats-0001-peer-dedup-map.patchdefects/nats-server/patch/nats-server-0001-diff-routes-map.patchdefects/nats-server/patch/nats-server-0002-diff-routes-map.patch
Three patches across jetstream_cluster.go, reload.go, and consumer.go.
What We Ask
Patches ready for review.
- Confirm receipt and assign a GitHub issue reference (nats-io/nats-server).
- Assess severity — nats-0001 fires during stream replica changes; nats-server-0001 fires on every config reload; nats-server-0002 fires on every consumer create/update/recover.
- Coordinate a disclosure date — we target 90 days from first contact.
- We will credit the NATS team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.