5.1 KiB
Podman — CWE-407 Disclosure Brief
2026-03-27 · Patch available — awaiting upstream merge
Finding
Two O(n²) defects in Podman's Kubernetes translation layer and runtime pod tracking. Both use slices.Contains over growing slices in loops that iterate the same collection. Both patched. Speedups measured at 49×–50×.
The Defects
podman-0001 (PATCHED — HIGH): libpod/kube.go:1280
// determineCapAddDropFromCapabilities() — called per container during kube generate:
for _, cap := range containerCaps { // O(n) outer
if slices.Contains(defaultCaps, cap) { ... } // O(n) scan — cap-set diff
if slices.Contains(otherCaps, cap) { ... } // O(n) scan
}
// O(n²) total — n = number of Linux capabilities in each set
determineCapAddDropFromCapabilities() computes the capability add/drop diff between container capabilities and default/other capability sets using slices.Contains inside a loop. For n capabilities per set: O(n²). Fix: pre-build map[string]bool for defaultCaps and otherCaps before the loop → O(n). Measured ratio: 50×.
podman-0002 (PATCHED — HIGH): libpod/runtime_pod.go:147
// GetRunningPods() — pod-ID dedup over container list:
for _, ctr := range allContainers {
podID := ctr.PodID()
if slices.Contains(pods, podID) { // O(n) scan — n = pods accumulated so far
continue
}
pods = append(pods, podID) // growing slice — O(n²) total
}
GetRunningPods() deduplicates pod IDs by calling slices.Contains on the growing pods slice for each container. For C containers across P distinct pod IDs: O(C × P). Since P grows toward C, worst case is O(C²). Fix: map[string]bool for O(C) total. Measured ratio: 49×.
Complexity Proof
podman-0001: Let n = number of Linux capabilities (typically 40+ for custom cap sets, up to ~60 defined in the kernel ABI).
- Defective: two
slices.Containscalls per outer iteration → 2n comparisons per capability → 2n² total. - Fixed: two
map[string]boollookups per iteration → O(2n) total (n to build each map + n lookups). - At n=50 capabilities: defective=5,000 comparisons, fixed=100. 50× measured ratio.
podman-0002: Let C = number of containers returned by the list, P = number of distinct pod IDs (P ≤ C).
- Defective:
slices.Contains(pods, podID)wherepodsgrows from 0 to P → 0+1+2+...+(P-1) = P(P-1)/2 comparisons → O(P²) ≤ O(C²). - Fixed:
map[string]bool→ O(1) per check → O(C) total. - At C=P=49 (49 containers, each in a distinct pod): defective=1,176 comparisons, fixed=49. 49× measured ratio.
Impact
podman-0001 affects podman kube generate — the command that translates Podman containers and pods into Kubernetes YAML manifests. This is a key migration and integration workflow for users moving workloads between Podman and Kubernetes. The defect fires once per container per kube generate invocation.
podman-0002 affects GetRunningPods() — a runtime function called by pod management operations including podman pod ps, podman pod inspect, and pod-aware container listing. It fires on every invocation in environments with many containers spread across many pods.
Podman is the primary rootless container runtime for RHEL/Fedora/CentOS and is widely used as a Docker alternative in security-sensitive environments.
The Fix
podman-0001: Pre-build maps for both capability sets before the diff loop:
// Before
for _, cap := range containerCaps {
if slices.Contains(defaultCaps, cap) { ... } // O(n) per cap
if slices.Contains(otherCaps, cap) { ... } // O(n) per cap
}
// After
// CWE-407 fix: pre-built maps for O(1) cap lookup instead of O(n) slices.Contains.
defaultCapSet := make(map[string]bool, len(defaultCaps))
for _, c := range defaultCaps { defaultCapSet[c] = true }
otherCapSet := make(map[string]bool, len(otherCaps))
for _, c := range otherCaps { otherCapSet[c] = true }
for _, cap := range containerCaps {
if defaultCapSet[cap] { ... } // O(1)
if otherCapSet[cap] { ... } // O(1)
}
podman-0002: Replace the growing-slice dedup with a map:
// Before
for _, ctr := range allContainers {
podID := ctr.PodID()
if slices.Contains(pods, podID) { continue } // O(pods) per container
pods = append(pods, podID)
}
// After
// CWE-407 fix: map[string]bool for O(C) dedup instead of O(C²) growing-slice scan.
seen := make(map[string]bool)
for _, ctr := range allContainers {
podID := ctr.PodID()
if seen[podID] { continue } // O(1)
seen[podID] = true
pods = append(pods, podID)
}
Patch
defects/podman/patch/podman-0001-0002-kube-caps-pod-dedup-map.patch
What We Ask
- Confirm receipt and assign a GitHub Security Advisory or issue reference (containers/podman).
- Validate the patch against the kube generation and runtime pod test suites.
- Assess CVE eligibility — both defects measured at 49×–50× under realistic workloads.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
Contact: see cover email. This brief is confidential until coordinated disclosure.