]> Cypherpunks repositories - gostls13.git/commitdiff
unique: don't retain uncloned input as key
authorMichael Anthony Knyszek <mknyszek@google.com>
Wed, 4 Sep 2024 16:46:33 +0000 (16:46 +0000)
committerGopher Robot <gobot@golang.org>
Wed, 4 Sep 2024 18:13:29 +0000 (18:13 +0000)
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>

src/unique/handle.go
src/unique/handle_test.go

index 96d8fedb0cabe6c519feab9132d1731f2b18f748..abc620f60fe14e49f9fc57f7d253f599a40a27a2 100644 (file)
@@ -50,13 +50,13 @@ func Make[T comparable](value T) Handle[T] {
                toInsert     *T // Keep this around to keep it alive.
                toInsertWeak weak.Pointer[T]
        )
-       newValue := func() weak.Pointer[T] {
+       newValue := func() (T, weak.Pointer[T]) {
                if toInsert == nil {
                        toInsert = new(T)
                        *toInsert = clone(value, &m.cloneSeq)
                        toInsertWeak = weak.Make(toInsert)
                }
-               return toInsertWeak
+               return *toInsert, toInsertWeak
        }
        var ptr *T
        for {
@@ -64,7 +64,8 @@ func Make[T comparable](value T) Handle[T] {
                wp, ok := m.Load(value)
                if !ok {
                        // Try to insert a new value into the map.
-                       wp, _ = m.LoadOrStore(value, newValue())
+                       k, v := newValue()
+                       wp, _ = m.LoadOrStore(k, v)
                }
                // Now that we're sure there's a value in the map, let's
                // try to get the pointer we need out of it.
index ffb2be42b127453e15da1989b93d9608f0da0a67..7d12edd4679f5bb6f0e1d408c9809ac8e2c898ee 100644 (file)
@@ -9,7 +9,10 @@ import (
        "internal/abi"
        "reflect"
        "runtime"
+       "strings"
        "testing"
+       "time"
+       "unsafe"
 )
 
 // 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) {
        }
        t.Errorf("failed to drain internal maps of %v", value)
 }
+
+func TestMakeClonesStrings(t *testing.T) {
+       s := strings.Clone("abcdefghijklmnopqrstuvwxyz") // N.B. Must be big enough to not be tiny-allocated.
+       ran := make(chan bool)
+       runtime.SetFinalizer(unsafe.StringData(s), func(_ *byte) {
+               ran <- true
+       })
+       h := Make(s)
+
+       // Clean up s (hopefully) and run the finalizer.
+       runtime.GC()
+
+       select {
+       case <-time.After(1 * time.Second):
+               t.Fatal("string was improperly retained")
+       case <-ran:
+       }
+       runtime.KeepAlive(h)
+}