]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: consistently seed fastrand state across archs
authorCarlo Alberto Ferraris <cafxx@strayorange.com>
Sat, 26 Oct 2019 07:26:59 +0000 (16:26 +0900)
committerKeith Randall <khr@golang.org>
Tue, 12 Nov 2019 21:40:12 +0000 (21:40 +0000)
Some, but not all, architectures mix in OS-provided random seeds when
initializing the fastrand state. The others have TODOs saying we need
to do the same. Lift that logic up in the architecture-independent
part, and use memhash to mix the seed instead of a simple addition.

Previously, dumping the fastrand state at initialization would yield
something like the following on linux-amd64, where the values in the
first column do not change between runs (as thread IDs are sequential
and always start at 0), and the values in the second column, while
changing every run, are pretty correlated:

first run:

0x0 0x44d82f1c
0x5f356495 0x44f339de
0xbe6ac92a 0x44f91cd8
0x1da02dbf 0x44fd91bc
0x7cd59254 0x44fee8a4
0xdc0af6e9 0x4547a1e0
0x3b405b7e 0x474c76fc
0x9a75c013 0x475309dc
0xf9ab24a8 0x4bffd075

second run:

0x0 0xa63fc3eb
0x5f356495 0xa6648dc2
0xbe6ac92a 0xa66c1c59
0x1da02dbf 0xa671bce8
0x7cd59254 0xa70e8287
0xdc0af6e9 0xa7129d2e
0x3b405b7e 0xa7379e2d
0x9a75c013 0xa7e4c64c
0xf9ab24a8 0xa7ecce07

With this change, we get initial states that appear to be much more
unpredictable, both within the same run as well as between runs:

0x11bddad7 0x97241c63
0x553dacc6 0x2bcd8523
0x62c01085 0x16413d92
0x6f40e9e6 0x7a138de6
0xa4898053 0x70d816f0
0x5ca5b433 0x188a395b
0x62778ca9 0xd462c3b5
0xd6e160e4 0xac9b4bd
0xb9571d65 0x597a981d

Change-Id: Ib22c530157d74200df0083f830e0408fd4aaea58
Reviewed-on: https://go-review.googlesource.com/c/go/+/203439
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
15 files changed:
src/runtime/os_darwin_arm.go
src/runtime/os_darwin_arm64.go
src/runtime/os_freebsd_arm.go
src/runtime/os_freebsd_arm64.go
src/runtime/os_js.go
src/runtime/os_linux_arm.go
src/runtime/os_linux_arm64.go
src/runtime/os_linux_mips64x.go
src/runtime/os_linux_mipsx.go
src/runtime/os_netbsd_arm.go
src/runtime/os_netbsd_arm64.go
src/runtime/os_openbsd_arm.go
src/runtime/os_openbsd_arm64.go
src/runtime/os_plan9_arm.go
src/runtime/proc.go

index ee1bd174f1be6d9c1518e910870b5e16b5775cbd..2703e3cff8c9416ffc0972bc4dbb7c6ce6cefc72 100644 (file)
@@ -19,6 +19,5 @@ func checkgoarm() {
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // TODO: need more entropy to better seed fastrand.
        return nanotime()
 }
index 8de132d8e2fab9419705cc1faefd250acb69d21f..b808150de01dcde36567b32e1fee908fe5e9b7e5 100644 (file)
@@ -8,6 +8,5 @@ package runtime
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // TODO: need more entropy to better seed fastrand.
        return nanotime()
 }
index 3edd381302d7df9567b28d3c60d0dcff436507ae..3feaa5e225a0ab8307cf97dbf0bd127814e536c8 100644 (file)
@@ -44,6 +44,5 @@ func archauxv(tag, val uintptr) {
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // TODO: need more entropy to better seed fastrand.
        return nanotime()
 }
index 800bd2fa6e4636b2df77e269df83625c0d589c2d..51ebf9d4784867f3778331e4dff0228064d81f53 100644 (file)
@@ -151,6 +151,5 @@ func extractBits(data uint64, start, end uint) uint {
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed fastrand().
        // nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // TODO: need more entropy to better seed fastrand.
        return nanotime()
 }
index 3738c9b237ff382f6d6f839cde7415d84242688a..ff0ee3aa6be8dfd4ae3adfbb82c9eaeaa4f18031 100644 (file)
@@ -131,7 +131,6 @@ func os_sigpipe() {
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // TODO: need more entropy to better seed fastrand.
        return nanotime()
 }
 
index 207b0e4d4d125084e5b405629439170dbcade1d1..5f89c30f7ab54eefe1989cdc3f08516c5a5415d2 100644 (file)
@@ -11,8 +11,6 @@ const (
        _HWCAP_VFPv3 = 1 << 13 // introduced in 2.6.30
 )
 
-var randomNumber uint32
-
 func checkgoarm() {
        // On Android, /proc/self/auxv might be unreadable and hwcap won't
        // reflect the CPU capabilities. Assume that every Android arm device
@@ -34,13 +32,6 @@ func checkgoarm() {
 
 func archauxv(tag, val uintptr) {
        switch tag {
-       case _AT_RANDOM:
-               // sysargs filled in startupRandomData, but that
-               // pointer may not be word aligned, so we must treat
-               // it as a byte array.
-               randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 |
-                       uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24
-
        case _AT_HWCAP:
                cpu.HWCap = uint(val)
        case _AT_HWCAP2:
@@ -52,6 +43,5 @@ func archauxv(tag, val uintptr) {
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed fastrand().
        // nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // randomNumber provides better seeding of fastrand.
-       return nanotime() + int64(randomNumber)
+       return nanotime()
 }
index 2d6f68bdd9e50eea9c62ce8d3ff7bfd4adf0f647..b51bc8882012d23548d26baa597c2af43a9f37db 100644 (file)
@@ -8,17 +8,8 @@ package runtime
 
 import "internal/cpu"
 
-var randomNumber uint32
-
 func archauxv(tag, val uintptr) {
        switch tag {
-       case _AT_RANDOM:
-               // sysargs filled in startupRandomData, but that
-               // pointer may not be word aligned, so we must treat
-               // it as a byte array.
-               randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 |
-                       uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24
-
        case _AT_HWCAP:
                // arm64 doesn't have a 'cpuid' instruction equivalent and relies on
                // HWCAP/HWCAP2 bits for hardware capabilities.
@@ -40,6 +31,5 @@ func archauxv(tag, val uintptr) {
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed fastrand().
        // nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // randomNumber provides better seeding of fastrand.
-       return nanotime() + int64(randomNumber)
+       return nanotime()
 }
index 0d7b84dcee65c4e505462eb1e400631b46d99785..59d2a8f2c6efcdcf34ef0dcbe69e3963cf2ec038 100644 (file)
@@ -7,25 +7,14 @@
 
 package runtime
 
-var randomNumber uint32
-
 func archauxv(tag, val uintptr) {
-       switch tag {
-       case _AT_RANDOM:
-               // sysargs filled in startupRandomData, but that
-               // pointer may not be word aligned, so we must treat
-               // it as a byte array.
-               randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 |
-                       uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24
-       }
 }
 
 //go:nosplit
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed fastrand().
        // nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // randomNumber provides better seeding of fastrand.
-       return nanotime() + int64(randomNumber)
+       return nanotime()
 }
 
 const (
index e0548ecc7982abab4a3ec7807d7c62c4b1e1b722..ccdc3a7fe56fd38315f01cfb8c6f851ac0d4336c 100644 (file)
@@ -7,25 +7,14 @@
 
 package runtime
 
-var randomNumber uint32
-
 func archauxv(tag, val uintptr) {
-       switch tag {
-       case _AT_RANDOM:
-               // sysargs filled in startupRandomData, but that
-               // pointer may not be word aligned, so we must treat
-               // it as a byte array.
-               randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 |
-                       uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24
-       }
 }
 
 //go:nosplit
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed fastrand().
        // nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // randomNumber provides better seeding of fastrand1.
-       return nanotime() + int64(randomNumber)
+       return nanotime()
 }
 
 const (
index 95603da64394b02bc3d48c9ce4a88f74f56e50f3..b5ec23e45b0a8833154279d37920e42ad6673802 100644 (file)
@@ -30,6 +30,5 @@ func checkgoarm() {
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // TODO: need more entropy to better seed fastrand.
        return nanotime()
 }
index fd81eb7557d21c5e44137d47d84fcba87c6a6780..8d21b0a430f9abeecc833cf6dcb4f1e459f698b0 100644 (file)
@@ -19,6 +19,5 @@ func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintp
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // TODO: need more entropy to better seed fastrand.
        return nanotime()
 }
index be2e1e9959da60f2074ed78b12c24944f81c9a6b..0a2409676c18009be2f41c14fba53e8f3261ea7b 100644 (file)
@@ -19,6 +19,5 @@ func checkgoarm() {
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // TODO: need more entropy to better seed fastrand.
        return nanotime()
 }
index f15a95b653f6db3e36b1b1ed1dba40f38e7b35a0..d559a2a3e5adc61fae6937fe180819838e48256b 100644 (file)
@@ -12,7 +12,6 @@ import (
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // TODO: need more entropy to better seed fastrand.
        return nanotime()
 }
 
index fdce1e7a352d6122044e3dd0d06ca7c11a44f7e2..f165a34151f04181221a03142cc322d1c5868cf2 100644 (file)
@@ -12,6 +12,5 @@ func checkgoarm() {
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // TODO: need more entropy to better seed fastrand.
        return nanotime()
 }
index 56e9530ab62c4762dd3eb67a1660da2099d1343a..3252173c0a2323c90ccdee24a29aa011cb79e31c 100644 (file)
@@ -543,6 +543,7 @@ func schedinit() {
        moduledataverify()
        stackinit()
        mallocinit()
+       fastrandinit() // must run before mcommoninit
        mcommoninit(_g_.m)
        cpuinit()       // must run before alginit
        alginit()       // maps must not be used before this call
@@ -620,8 +621,8 @@ func mcommoninit(mp *m) {
        sched.mnext++
        checkmcount()
 
-       mp.fastrand[0] = 1597334677 * uint32(mp.id)
-       mp.fastrand[1] = uint32(cputicks())
+       mp.fastrand[0] = uint32(int64Hash(uint64(mp.id), fastrandseed))
+       mp.fastrand[1] = uint32(int64Hash(uint64(cputicks()), ^fastrandseed))
        if mp.fastrand[0]|mp.fastrand[1] == 0 {
                mp.fastrand[1] = 1
        }
@@ -646,6 +647,13 @@ func mcommoninit(mp *m) {
        }
 }
 
+var fastrandseed uintptr
+
+func fastrandinit() {
+       s := (*[unsafe.Sizeof(fastrandseed)]byte)(unsafe.Pointer(&fastrandseed))[:]
+       getRandomData(s)
+}
+
 // Mark gp ready to run.
 func ready(gp *g, traceskip int, next bool) {
        if trace.enabled {