Skip to content

Commit 21ac23a

Browse files
mknyszekgopherbot
authored andcommitted
unique: don't retain uncloned input as key
Currently the unique package tries to clone strings that get stored in its internal map to avoid retaining large strings. However, this falls over entirely due to the fact that the original string is *still* stored in the map as a key. Whoops. Fix this by storing the cloned value in the map instead. This change also adds a test which fails without this change. Change-Id: I1a6bb68ed79b869ea12ab6be061a5ae4b4377ddb Reviewed-on: https://go-review.googlesource.com/c/go/+/610738 Reviewed-by: Michael Pratt <mpratt@google.com> Auto-Submit: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1 parent 79fd633 commit 21ac23a

File tree

2 files changed

+26
-3
lines changed

2 files changed

+26
-3
lines changed

src/unique/handle.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,22 @@ func Make[T comparable](value T) Handle[T] {
5050
toInsert *T // Keep this around to keep it alive.
5151
toInsertWeak weak.Pointer[T]
5252
)
53-
newValue := func() weak.Pointer[T] {
53+
newValue := func() (T, weak.Pointer[T]) {
5454
if toInsert == nil {
5555
toInsert = new(T)
5656
*toInsert = clone(value, &m.cloneSeq)
5757
toInsertWeak = weak.Make(toInsert)
5858
}
59-
return toInsertWeak
59+
return *toInsert, toInsertWeak
6060
}
6161
var ptr *T
6262
for {
6363
// Check the map.
6464
wp, ok := m.Load(value)
6565
if !ok {
6666
// Try to insert a new value into the map.
67-
wp, _ = m.LoadOrStore(value, newValue())
67+
k, v := newValue()
68+
wp, _ = m.LoadOrStore(k, v)
6869
}
6970
// Now that we're sure there's a value in the map, let's
7071
// try to get the pointer we need out of it.

src/unique/handle_test.go

+22
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import (
99
"internal/abi"
1010
"reflect"
1111
"runtime"
12+
"strings"
1213
"testing"
14+
"time"
15+
"unsafe"
1316
)
1417

1518
// Set up special types. Because the internal maps are sharded by type,
@@ -110,3 +113,22 @@ func checkMapsFor[T comparable](t *testing.T, value T) {
110113
}
111114
t.Errorf("failed to drain internal maps of %v", value)
112115
}
116+
117+
func TestMakeClonesStrings(t *testing.T) {
118+
s := strings.Clone("abcdefghijklmnopqrstuvwxyz") // N.B. Must be big enough to not be tiny-allocated.
119+
ran := make(chan bool)
120+
runtime.SetFinalizer(unsafe.StringData(s), func(_ *byte) {
121+
ran <- true
122+
})
123+
h := Make(s)
124+
125+
// Clean up s (hopefully) and run the finalizer.
126+
runtime.GC()
127+
128+
select {
129+
case <-time.After(1 * time.Second):
130+
t.Fatal("string was improperly retained")
131+
case <-ran:
132+
}
133+
runtime.KeepAlive(h)
134+
}

0 commit comments

Comments
 (0)