1
0
mirror of https://github.com/golang/go synced 2024-11-14 17:20:21 -07:00

runtime: overwrite startupRand instead of clearing it

AT_RANDOM is unfortunately used by libc before we run (so make sure it's
not cleared) but also is available to cgo programs after we did. It
would be unfortunate if a cgo program assumed it could use AT_RANDOM but
instead found all zeroes there.

Change-Id: I82eff34d8cf5a499b439052b7827b8ef7cabc21d
Reviewed-on: https://go-review.googlesource.com/c/go/+/608437
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
This commit is contained in:
Filippo Valsorda 2024-08-26 20:00:10 +02:00
parent 311372c53c
commit 9a44b8e15a
3 changed files with 46 additions and 6 deletions

View File

@ -296,13 +296,19 @@ func sysargs(argc int32, argv **byte) {
var secureMode bool
func sysauxv(auxv []uintptr) (pairs int) {
// Process the auxiliary vector entries provided by the kernel when the
// program is executed. See getauxval(3).
var i int
for ; auxv[i] != _AT_NULL; i += 2 {
tag, val := auxv[i], auxv[i+1]
switch tag {
case _AT_RANDOM:
// The kernel provides a pointer to 16-bytes
// worth of random data.
// The kernel provides a pointer to 16 bytes of cryptographically
// random data. Note that in cgo programs this value may have
// already been used by libc at this point, and in particular glibc
// and musl use the value as-is for stack and pointer protector
// cookies from libc_start_main and/or dl_start. Also, cgo programs
// may use the value after we do.
startupRand = (*[16]byte)(unsafe.Pointer(val))[:]
case _AT_PAGESZ:
@ -354,6 +360,8 @@ func osinit() {
var urandom_dev = []byte("/dev/urandom\x00")
func readRandom(r []byte) int {
// Note that all supported Linux kernels should provide AT_RANDOM which
// populates startupRand, so this fallback should be unreachable.
fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
closefd(fd)

View File

@ -139,6 +139,9 @@ func osinit() {
physPageSize = getPageSize()
}
// TODO(#69781): set startupRand using the .openbsd.randomdata ELF section.
// See SPECS.randomdata.
var urandom_dev = []byte("/dev/urandom\x00")
//go:nosplit

View File

@ -7,6 +7,7 @@
package runtime
import (
"internal/byteorder"
"internal/chacha8rand"
"internal/goarch"
"internal/runtime/math"
@ -41,14 +42,15 @@ func randinit() {
}
seed := &globalRand.seed
if startupRand != nil {
if len(startupRand) >= 16 &&
// Check that at least the first two words of startupRand weren't
// cleared by any libc initialization.
!allZero(startupRand[:8]) && !allZero(startupRand[8:16]) {
for i, c := range startupRand {
seed[i%len(seed)] ^= c
}
clear(startupRand)
startupRand = nil
} else {
if readRandom(seed[:]) != len(seed) {
if readRandom(seed[:]) != len(seed) || allZero(seed[:]) {
// readRandom should never fail, but if it does we'd rather
// not make Go binaries completely unusable, so make up
// some random data based on the current time.
@ -58,6 +60,25 @@ func randinit() {
}
globalRand.state.Init(*seed)
clear(seed[:])
if startupRand != nil {
// Overwrite startupRand instead of clearing it, in case cgo programs
// access it after we used it.
for len(startupRand) > 0 {
buf := make([]byte, 8)
for {
if x, ok := globalRand.state.Next(); ok {
byteorder.BePutUint64(buf, x)
break
}
globalRand.state.Refill()
}
n := copy(startupRand, buf)
startupRand = startupRand[n:]
}
startupRand = nil
}
globalRand.init = true
unlock(&globalRand.lock)
}
@ -88,6 +109,14 @@ func readTimeRandom(r []byte) {
}
}
func allZero(b []byte) bool {
var acc byte
for _, x := range b {
acc |= x
}
return acc == 0
}
// bootstrapRand returns a random uint64 from the global random generator.
func bootstrapRand() uint64 {
lock(&globalRand.lock)