1
0
mirror of https://github.com/golang/go synced 2024-11-15 03:00:36 -07:00

runtime: use bootstrapRand to initialize hashkey

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>
This commit is contained in:
Michael Pratt 2024-04-18 12:42:43 -04:00
parent 1a0b86375f
commit 1a3682b4c1
2 changed files with 81 additions and 1 deletions

View File

@ -391,7 +391,7 @@ func alginit() {
return return
} }
for i := range hashkey { 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
} }
} }

View File

@ -8,7 +8,9 @@ import (
"fmt" "fmt"
"internal/abi" "internal/abi"
"internal/goarch" "internal/goarch"
"internal/testenv"
"math" "math"
"os"
"reflect" "reflect"
"runtime" "runtime"
"sort" "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)
}
})
}