]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: overwrite startupRand instead of clearing it
authorFilippo Valsorda <filippo@golang.org>
Mon, 26 Aug 2024 18:00:10 +0000 (20:00 +0200)
committerFilippo Valsorda <filippo@golang.org>
Mon, 7 Oct 2024 15:35:02 +0000 (15:35 +0000)
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>
src/runtime/os_linux.go
src/runtime/os_openbsd.go
src/runtime/rand.go

index 979761cc6a888b9719c66f77a9b13707a2cc7cee..8f5cf6db8abe3ce2a3ee53fc138ba9bb0ae8afaf 100644 (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)
index 9a21d6a8d0522851134ffbb1733a577960e8aaa1..574bfa8b1708be7861d3f605ce1d4c58356338ef 100644 (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
index 11be6552aa3314a97bced18420dfd4d8c7a4aece..2e44858ee21d24e7bbcdb7a9d2ef331e4b6d6733 100644 (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)