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.
4.8 KiB
MinIO — CWE-407 Disclosure Brief
2026-04-13 · Patches available — awaiting upstream merge
Finding
Three O(n²) defects in MinIO across disk healing, pool decommissioning, and site replication. All patched. Patches ready for upstream review. Two defects fire during bucket-level healing/decommission loops; one fires during cross-site replication heal comparisons.
The Defects
minio-0001 (PATCHED — MEDIUM): cmd/background-newdisks-heal-ops.go
// In healingTracker.isHealed() — fires per bucket during heal loop:
func (h *healingTracker) isHealed(bucket string) bool {
h.mu.RLock()
defer h.mu.RUnlock()
return slices.Contains(h.HealedBuckets, bucket) // O(H) linear scan
}
HealedBuckets grows as healing progresses. The heal loop iterates all buckets (B) and calls isHealed() for each, which scans HealedBuckets (H) with slices.Contains. As healing progresses, H grows toward B, making total cost O(B²). Ironically, setQueuedBuckets() already builds a set.CreateStringSet(HealedBuckets...) for the same data, but isHealed() does not use it.
minio-0002 (PATCHED — MEDIUM): cmd/erasure-server-pool-decom.go
// In isBucketDecommissioned() — fires per bucket during decommission:
func (pd *PoolDecommissionInfo) isBucketDecommissioned(bucket string) bool {
return slices.Contains(pd.DecommissionedBuckets, bucket) // O(D) linear scan
}
Same pattern as minio-0001 in the decommission path. decommissionInBackground() iterates pending buckets (P), each calling isBucketDecommissioned which scans DecommissionedBuckets (D). Total: O(P×D). As decommission progresses, D grows toward total buckets.
minio-0003 (PATCHED — MEDIUM): cmd/site-replication.go:5677,5698
// In isGroupDescEqual() / isUserInfoEqual() — fires per group/user during site-replication heal:
for _, v1 := range g1.Members {
if slices.Contains(g2.Members, v1) {
found = true
}
if !found {
return false
}
}
Both isGroupDescEqual() and isUserInfoEqual() compare two string slices for set equality by iterating one and calling slices.Contains on the other — O(M²) where M = members or groups. In LDAP-backed deployments, groups can have 1,000+ members, producing 1M+ operations per comparison. Called per-user and per-group across deployment sites during site replication healing.
Complexity Proof
minio-0001/minio-0002: At B=500 buckets:
- Defective: 500 × (500 / 2) = ~125,000 comparisons
- Fixed: 500 × 1 = 500 map lookups
- 250× op reduction. At B=10,000 (large deployments): 5,000× op reduction.
minio-0003: At M=500 members per group:
- Defective: 500 × 500 = 250,000 comparisons per group comparison
- Fixed: 500 + 500 = 1,000 operations (map build + probe)
- 250× op reduction.
Impact
MinIO powers object storage for thousands of organizations — from on-premise Kubernetes deployments to large-scale data lakes. minio-0001 fires during disk healing after a drive replacement or new node addition — a routine cluster maintenance operation. At 10,000+ buckets (common in multi-tenant deployments), the O(B²) scan produces visible healing delays. minio-0002 fires during pool decommissioning, another routine capacity management operation. minio-0003 fires during site replication healing across geographically distributed MinIO clusters — LDAP-backed deployments with large groups compound the quadratic cost per user and per group comparison.
The Fix
minio-0001: Add a healedSet map[string]struct{} field to healingTracker, maintained alongside HealedBuckets. Replace slices.Contains with map lookup.
minio-0002: Add a decomBucketSet map[string]struct{} field to PoolDecommissionInfo, maintained alongside DecommissionedBuckets. Lazy-rebuild the set on first call after deserialization.
minio-0003: Pre-build a map[string]struct{} from one slice, then probe with the other. Applied to both isGroupDescEqual() and isUserInfoEqual().
Patch
Fixes available:
defects/minio/patch/minio-0001-heal-tracker-isHealed-linear-scan.patchdefects/minio/patch/minio-0002-decom-isBucketDecommissioned-linear-scan.patchdefects/minio/patch/minio-0003-site-replication-set-equality-linear-scan.patch
Three patches across background-newdisks-heal-ops.go, erasure-server-pool-decom.go, and site-replication.go.
What We Ask
Patches ready for review.
- Confirm receipt and assign a GitHub issue reference (minio/minio).
- Assess severity — minio-0001 fires during disk healing; minio-0002 fires during pool decommission; minio-0003 fires during site replication heal.
- Coordinate a disclosure date — we target 90 days from first contact.
- We will credit the MinIO team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.