From 6f2406b0b67c15d630d746f91ee3177914611b37 Mon Sep 17 00:00:00 2001 From: Klaus Post Date: Mon, 12 Jun 2023 09:17:11 -0700 Subject: [PATCH] fix: protect ReplicationStats against concurrent map iteration and write crash (#17403) --- cmd/data-scanner.go | 2 +- cmd/data-usage-cache.go | 42 +++++++++++++++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/cmd/data-scanner.go b/cmd/data-scanner.go index c45320ceb..334ee11e7 100644 --- a/cmd/data-scanner.go +++ b/cmd/data-scanner.go @@ -373,7 +373,7 @@ func (f *folderScanner) sendUpdate() { } if flat := f.updateCache.sizeRecursive(f.newCache.Info.Name); flat != nil { select { - case f.updates <- *flat: + case f.updates <- flat.clone(): default: } f.lastUpdate = time.Now() diff --git a/cmd/data-usage-cache.go b/cmd/data-usage-cache.go index 117d1dd29..344d78b01 100644 --- a/cmd/data-usage-cache.go +++ b/cmd/data-usage-cache.go @@ -88,6 +88,20 @@ func (ats *allTierStats) merge(other *allTierStats) { } } +func (ats *allTierStats) clone() *allTierStats { + if ats == nil { + return nil + } + dst := *ats + if dst.Tiers != nil { + dst.Tiers = make(map[string]tierStats, len(dst.Tiers)) + for tier, st := range dst.Tiers { + dst.Tiers[tier] = st + } + } + return &dst +} + func (ats *allTierStats) adminStats(stats map[string]madmin.TierStats) map[string]madmin.TierStats { if ats == nil { return stats @@ -168,6 +182,25 @@ type replicationAllStatsV1 struct { ReplicaSize uint64 `msg:"ReplicaSize,omitempty"` } +// clone creates a deep-copy clone. +func (r *replicationAllStats) clone() *replicationAllStats { + if r == nil { + return nil + } + + // Shallow copy + dst := *r + + // Copy individual targets. + if dst.Targets != nil { + dst.Targets = make(map[string]replicationStats, len(dst.Targets)) + for k, v := range r.Targets { + dst.Targets[k] = v + } + } + return &dst +} + //msgp:encode ignore dataUsageEntryV2 dataUsageEntryV3 dataUsageEntryV4 dataUsageEntryV5 dataUsageEntryV6 //msgp:marshal ignore dataUsageEntryV2 dataUsageEntryV3 dataUsageEntryV4 dataUsageEntryV5 dataUsageEntryV6 @@ -413,14 +446,11 @@ func (e dataUsageEntry) clone() dataUsageEntry { e.Children = ch } if e.ReplicationStats != nil { - // Copy to new struct - r := *e.ReplicationStats - e.ReplicationStats = &r + // Clone ReplicationStats + e.ReplicationStats = e.ReplicationStats.clone() } if e.AllTierStats != nil { - ats := newAllTierStats() - ats.merge(e.AllTierStats) - e.AllTierStats = ats + e.AllTierStats = e.AllTierStats.clone() } return e }