java-topology/defects/minio/patch/minio-0003-site-replication-set-equality-linear-scan.patch

72 lines
2.1 KiB
Diff

# UNDF: UNDF-2026-000000770
# UNDF: (leave blank)
# minio-0003: isGroupDescEqual/isUserInfoEqual use slices.Contains in loop → O(M²)
#
# File: cmd/site-replication.go
# Functions: isGroupDescEqual() line 5677, isUserInfoEqual() line 5698
#
# Both functions compare two string slices for set equality by iterating one
# slice and calling slices.Contains on the other — O(M²) where M is the
# number of members/groups. In LDAP-backed deployments, groups can have
# 1000+ members, making this 1M+ operations per comparison.
#
# Called from site replication healing (lines 5493, 5653) — per-user and
# per-group, across deployment sites.
#
# Fix: build a map[string]struct{} from one slice, then probe with the other.
# Severity: MEDIUM (O(M²) per group/user comparison during site-replication heal)
# Overhead ratio: ~250x at M=500
#
--- a/cmd/site-replication.go
+++ b/cmd/site-replication.go
@@ -5677,16 +5677,14 @@
func isGroupDescEqual(g1, g2 madmin.GroupDesc) bool {
if g1.Name != g2.Name ||
g1.Status != g2.Status ||
g1.Policy != g2.Policy {
return false
}
if len(g1.Members) != len(g2.Members) {
return false
}
- for _, v1 := range g1.Members {
- var found bool
- if slices.Contains(g2.Members, v1) {
- found = true
- }
- if !found {
+ memberSet := make(map[string]struct{}, len(g2.Members))
+ for _, m := range g2.Members {
+ memberSet[m] = struct{}{}
+ }
+ for _, v1 := range g1.Members {
+ if _, ok := memberSet[v1]; !ok {
return false
}
}
@@ -5698,16 +5696,14 @@
func isUserInfoEqual(u1, u2 madmin.UserInfo) bool {
if u1.PolicyName != u2.PolicyName ||
u1.Status != u2.Status ||
u1.SecretKey != u2.SecretKey {
return false
}
- for len(u1.MemberOf) != len(u2.MemberOf) {
+ if len(u1.MemberOf) != len(u2.MemberOf) {
return false
}
- for _, v1 := range u1.MemberOf {
- var found bool
- if slices.Contains(u2.MemberOf, v1) {
- found = true
- }
- if !found {
+ groupSet := make(map[string]struct{}, len(u2.MemberOf))
+ for _, g := range u2.MemberOf {
+ groupSet[g] = struct{}{}
+ }
+ for _, v1 := range u1.MemberOf {
+ if _, ok := groupSet[v1]; !ok {
return false
}
}