java-topology/whitepaper/outreach/nats-server.md
russell@unturf.com d1f82fd8e3 feat: add 8 outreach docs (26 defects) for batch 3
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.
2026-04-13 15:35:53 -04:00

5.1 KiB
Raw Blame History

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 != outer branch 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.patch
  • defects/nats-server/patch/nats-server-0001-diff-routes-map.patch
  • defects/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.

  1. Confirm receipt and assign a GitHub issue reference (nats-io/nats-server).
  2. 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.
  3. Coordinate a disclosure date — we target 90 days from first contact.
  4. 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.