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:
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)
physPageSize = getPageSize()
}
+// TODO(#69781): set startupRand using the .openbsd.randomdata ELF section.
+// See SPECS.randomdata.
+
var urandom_dev = []byte("/dev/urandom\x00")
//go:nosplit
package runtime
import (
+ "internal/byteorder"
"internal/chacha8rand"
"internal/goarch"
"internal/runtime/math"
}
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.
}
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)
}
}
}
+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)