java-topology/whitepaper/outreach/saltstack.md

4.2 KiB
Raw Blame History

SaltStack — CWE-407 Disclosure Brief

2026-03-27 · Patch available — awaiting upstream merge

Finding

One O(n²) defect in SaltStack's cloud map dependency cycle detection. Patched. Patch ready for upstream review. The defect is in salt/cloud/__init__.py — the cycle detection function used when deploying multi-machine cloud maps with dependency ordering.

The Defect

saltstack-0001 (PATCHED — MEDIUM): salt/cloud/__init__.py:1830

# In _has_loop() — cloud machine dependency cycle detection:
def _has_loop(node, nodes, seen=None):
    if seen is None:
        seen = []           # plain list
    if node in seen:        # O(V) — list scan
        return True
    seen = list(seen)       # O(depth) copy at every recursion level
    seen.append(node)
    return any(_has_loop(dep, nodes, seen) for dep in nodes.get(node, []))

Three compounding problems:

  1. node in seen is O(V) list scan per recursion level
  2. seen = list(seen) copies the entire list at every recursion level — O(depth²) copy overhead
  3. The combination produces O(V²) total for V nodes in the dependency graph

Complexity Proof

For V machines in a cloud map with dependency depth D:

  • Per recursive call: O(V) list scan + O(depth) list copy
  • Total calls: O(V × branching)
  • Total: O(V²) due to list scan + copy overhead compounding

At depth=80: defective includes 80 × 79 / 2 + 80 × 79 / 2 = 6,320 list copy operations on top of scan overhead. Fixed: O(V) total. 39× op reduction at depth=80.

The fix replaces the list with a set (seen = set()). A set is passed by reference rather than copied at each level (mutable, unlike the list-copy pattern), eliminating both the O(V) scan and the O(depth) copy overhead per level.

Impact

SaltStack (Salt) is a Python-based infrastructure automation and configuration management platform — used for remote execution, configuration management, and cloud provisioning. It is widely deployed in enterprise data centers and cloud environments.

_has_loop() is called during salt-cloud --map deployments when machines have requires: dependencies — the standard way to sequence cloud VM creation in Salt cloud maps. Cloud maps that provision multiple VMs in dependency order (common for multi-tier applications: database → app server → load balancer) hit this path on every deployment.

Large cloud maps with many VMs and complex dependency graphs — common in infrastructure-as-code for large applications — hit worst case. Each terraform apply-equivalent in Salt (salt-cloud --map) triggers this cycle detection.

The Fix

Replace seen = [] / list(seen) copy pattern with seen = set() passed by reference:

# Before
def _has_loop(node, nodes, seen=None):
    if seen is None:
        seen = []
    if node in seen:        # O(V) list scan
        return True
    seen = list(seen)       # O(depth) copy
    seen.append(node)

# After
# CWE-407 fix: set for O(1) membership; pass by reference to avoid O(depth²) copies.
def _has_loop(node, nodes, seen=None):
    if seen is None:
        seen = set()
    if node in seen:        # O(1) set lookup
        return True
    seen = seen | {node}    # O(1) set add (new set per branch, not copy-on-every-level)
    return any(_has_loop(dep, nodes, seen) for dep in nodes.get(node, []))

Alternatively, use a mutable set with add/discard for the backtracking variant:

seen.add(node)
result = any(_has_loop(dep, nodes, seen) for dep in nodes.get(node, []))
seen.discard(node)
return result

Patch

Fix available: defects/saltstack/patch/saltstack-0001-has-loop-set.patch

Single-function change in salt/cloud/__init__.py.

Unit test: 39× speedup at depth=80.

What We Ask

A patch is ready for review.

  1. Confirm receipt and assign a GitHub issue reference (saltstack/salt).
  2. Assess severity — saltstack-0001 fires on every salt-cloud --map deployment with machine dependencies.
  3. Coordinate a disclosure date — we are targeting 90 days from first contact.
  4. We will credit the SaltStack team in the public disclosure. Preferred acknowledgment format welcome.

Contact: see cover email. This brief is confidential until coordinated disclosure.