maps: fix aliasing problems with Clone

Make sure to alloc+copy large keys and values instead of aliasing them,
when they might be updated by a future assignment.

Fixes #64474

Change-Id: Ie2226a81cf3897e4e2ee24472f2966d397ace53f
Reviewed-on: https://go-review.googlesource.com/c/go/+/546515
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Mauri de Souza Meneguzzo <mauri870@gmail.com>
This commit is contained in:
Keith Randall 2023-11-30 22:59:04 -08:00
parent 098f059d07
commit 16d3040a84
2 changed files with 76 additions and 4 deletions

View file

@ -1480,12 +1480,24 @@ func moveToBmap(t *maptype, h *hmap, dst *bmap, pos int, src *bmap) (*bmap, int)
dst.tophash[pos] = src.tophash[i]
if t.IndirectKey() {
*(*unsafe.Pointer)(dstK) = *(*unsafe.Pointer)(srcK)
srcK = *(*unsafe.Pointer)(srcK)
if t.NeedKeyUpdate() {
kStore := newobject(t.Key)
typedmemmove(t.Key, kStore, srcK)
srcK = kStore
}
// Note: if NeedKeyUpdate is false, then the memory
// used to store the key is immutable, so we can share
// it between the original map and its clone.
*(*unsafe.Pointer)(dstK) = srcK
} else {
typedmemmove(t.Key, dstK, srcK)
}
if t.IndirectElem() {
*(*unsafe.Pointer)(dstEle) = *(*unsafe.Pointer)(srcEle)
srcEle = *(*unsafe.Pointer)(srcEle)
eStore := newobject(t.Elem)
typedmemmove(t.Elem, eStore, srcEle)
*(*unsafe.Pointer)(dstEle) = eStore
} else {
typedmemmove(t.Elem, dstEle, srcEle)
}
@ -1509,14 +1521,14 @@ func mapclone2(t *maptype, src *hmap) *hmap {
fatal("concurrent map clone and map write")
}
if src.B == 0 {
if src.B == 0 && !(t.IndirectKey() && t.NeedKeyUpdate()) && !t.IndirectElem() {
// Quick copy for small maps.
dst.buckets = newobject(t.Bucket)
dst.count = src.count
typedmemmove(t.Bucket, dst.buckets, src.buckets)
return dst
}
//src.B != 0
if dst.B == 0 {
dst.buckets = newobject(t.Bucket)
}
@ -1564,6 +1576,8 @@ func mapclone2(t *maptype, src *hmap) *hmap {
continue
}
// oldB < dst.B, so a single source bucket may go to multiple destination buckets.
// Process entries one at a time.
for srcBmap != nil {
// move from oldBlucket to new bucket
for i := uintptr(0); i < bucketCnt; i++ {