]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: use bootstrapRand to initialize hashkey
authorMichael Pratt <mpratt@google.com>
Thu, 18 Apr 2024 16:42:43 +0000 (12:42 -0400)
committerMichael Pratt <mpratt@google.com>
Fri, 19 Apr 2024 16:34:32 +0000 (16:34 +0000)
The seed for rand is not initialized until after alginit. Before
initialization, rand returns a deterministic sequence, making hashkey
deterministic across processes.

Switch to bootstrapRand, like other early rand calls, such as
initialization of aeskeysched.

Fixes #66885.

Change-Id: I5023a9161232b49fda2ebd1d5f9338bbdd17b1fe
Reviewed-on: https://go-review.googlesource.com/c/go/+/580136
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
src/runtime/alg.go
src/runtime/map_test.go

index f5125d414fe4379c066cb62b00afe6136a8a0de6..aadd5f1002e8081418a9d7d927d39fb81eb42059 100644 (file)
@@ -391,7 +391,7 @@ func alginit() {
                return
        }
        for i := range hashkey {
-               hashkey[i] = uintptr(rand()) | 1 // make sure these numbers are odd
+               hashkey[i] = uintptr(bootstrapRand()) | 1 // make sure these numbers are odd
        }
 }
 
index 2c51236f16b9f98e0d2cdf8d4bc182a49db719dd..c29fb933ee49be2961bfea73a10758b7facce8bb 100644 (file)
@@ -8,7 +8,9 @@ import (
        "fmt"
        "internal/abi"
        "internal/goarch"
+       "internal/testenv"
        "math"
+       "os"
        "reflect"
        "runtime"
        "sort"
@@ -1464,3 +1466,81 @@ func TestMapValues(t *testing.T) {
                }
        }
 }
+
+func computeHash() uintptr {
+       var v struct{}
+       return runtime.MemHash(unsafe.Pointer(&v), 0, unsafe.Sizeof(v))
+}
+
+func subprocessHash(t *testing.T, env string) uintptr {
+       t.Helper()
+
+       cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0], "-test.run=^TestMemHashGlobalSeed$"))
+       cmd.Env = append(cmd.Env, "GO_TEST_SUBPROCESS_HASH=1")
+       if env != "" {
+               cmd.Env = append(cmd.Env, env)
+       }
+
+       out, err := cmd.Output()
+       if err != nil {
+               t.Fatalf("cmd.Output got err %v want nil", err)
+       }
+
+       s := strings.TrimSpace(string(out))
+       h, err := strconv.ParseUint(s, 10, 64)
+       if err != nil {
+               t.Fatalf("Parse output %q got err %v want nil", s, err)
+       }
+       return uintptr(h)
+}
+
+// memhash has unique per-process seeds, so hashes should differ across
+// processes.
+//
+// Regression test for https://go.dev/issue/66885.
+func TestMemHashGlobalSeed(t *testing.T) {
+       if os.Getenv("GO_TEST_SUBPROCESS_HASH") != "" {
+               fmt.Println(computeHash())
+               os.Exit(0)
+               return
+       }
+
+       testenv.MustHaveExec(t)
+
+       // aeshash and memhashFallback use separate per-process seeds, so test
+       // both.
+       t.Run("aes", func(t *testing.T) {
+               if !*runtime.UseAeshash {
+                       t.Skip("No AES")
+               }
+
+               h1 := subprocessHash(t, "")
+               t.Logf("%d", h1)
+               h2 := subprocessHash(t, "")
+               t.Logf("%d", h2)
+               h3 := subprocessHash(t, "")
+               t.Logf("%d", h3)
+
+               if h1 == h2 && h2 == h3 {
+                       t.Errorf("got duplicate hash %d want unique", h1)
+               }
+       })
+
+       t.Run("noaes", func(t *testing.T) {
+               env := ""
+               if *runtime.UseAeshash {
+                       env = "GODEBUG=cpu.aes=off"
+               }
+
+               h1 := subprocessHash(t, env)
+               t.Logf("%d", h1)
+               h2 := subprocessHash(t, env)
+               t.Logf("%d", h2)
+               h3 := subprocessHash(t, env)
+               t.Logf("%d", h3)
+
+               if h1 == h2 && h2 == h3 {
+                       t.Errorf("got duplicate hash %d want unique", h1)
+               }
+       })
+}