From e9003e2deb42df657a10726641fe3b8461567a03 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 18 Jan 2024 21:27:08 +0100 Subject: [PATCH] runtime: add runtime.rand function This function is needed for Go 1.22, and is used from various packages like math/rand. When there is no random number generator available, it falls back to a static sequence of numbers. I think this is fine, because as far as I can see it is only used for non-cryptographic needs. --- GNUmakefile | 1 + builder/musl.go | 1 + src/crypto/rand/rand_baremetal.go | 5 ++++- src/runtime/algorithm.go | 17 +++++++++++++++++ src/runtime/os_darwin.go | 11 +++++++++++ src/runtime/os_linux.go | 13 +++++++++++++ src/runtime/rand_hwrng.go | 16 ++++++++++++++++ src/runtime/rand_norng.go | 7 +++++++ src/runtime/runtime_nintendoswitch.go | 5 +++++ src/runtime/runtime_tinygowasm.go | 11 +++++++++++ src/runtime/runtime_windows.go | 16 ++++++++++++++++ 11 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 src/runtime/rand_hwrng.go create mode 100644 src/runtime/rand_norng.go diff --git a/GNUmakefile b/GNUmakefile index 5241d591..bac1691c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -838,6 +838,7 @@ endif @cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/legacy build/release/tinygo/lib/musl/src + @cp -rp lib/musl/src/linux build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src diff --git a/builder/musl.go b/builder/musl.go index 6ae1fda0..9b5c5270 100644 --- a/builder/musl.go +++ b/builder/musl.go @@ -119,6 +119,7 @@ var Musl = Library{ "internal/syscall_ret.c", "internal/vdso.c", "legacy/*.c", + "linux/*.c", "malloc/*.c", "malloc/mallocng/*.c", "mman/*.c", diff --git a/src/crypto/rand/rand_baremetal.go b/src/crypto/rand/rand_baremetal.go index cb606487..15fc916c 100644 --- a/src/crypto/rand/rand_baremetal.go +++ b/src/crypto/rand/rand_baremetal.go @@ -1,4 +1,7 @@ -//go:build nrf || stm32 || (sam && atsamd51) || (sam && atsame5x) || esp32c3 +//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 + +// If you update the above build constraint, you'll probably also need to update +// src/runtime/rand_hwrng.go. package rand diff --git a/src/runtime/algorithm.go b/src/runtime/algorithm.go index 11b39200..15ca2b7f 100644 --- a/src/runtime/algorithm.go +++ b/src/runtime/algorithm.go @@ -16,6 +16,8 @@ func rand_fastrand64() uint64 { } // This function is used by hash/maphash. +// This function isn't required anymore since Go 1.22, so should be removed once +// that becomes the minimum requirement. func fastrand() uint32 { xorshift32State = xorshift32(xorshift32State) return xorshift32State @@ -34,6 +36,8 @@ func xorshift32(x uint32) uint32 { } // This function is used by hash/maphash. +// This function isn't required anymore since Go 1.22, so should be removed once +// that becomes the minimum requirement. func fastrand64() uint64 { xorshift64State = xorshiftMult64(xorshift64State) return xorshift64State @@ -56,3 +60,16 @@ func memhash(p unsafe.Pointer, seed, s uintptr) uintptr { } return uintptr(hash32(p, s, seed)) } + +// Function that's called from various packages starting with Go 1.22. +func rand() uint64 { + // Return a random number from hardware, falling back to software if + // unavailable. + n, ok := hardwareRand() + if !ok { + // Fallback to static random number. + // Not great, but we can't do much better than this. + n = fastrand64() + } + return n +} diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go index 6f751102..d9894bc1 100644 --- a/src/runtime/os_darwin.go +++ b/src/runtime/os_darwin.go @@ -112,3 +112,14 @@ func findGlobals(found func(start, end uintptr)) { cmd = (*segmentLoadCommand)(unsafe.Add(unsafe.Pointer(cmd), cmd.cmdsize)) } } + +func hardwareRand() (n uint64, ok bool) { + n |= uint64(libc_arc4random()) + n |= uint64(libc_arc4random()) << 32 + return n, true +} + +// uint32_t arc4random(void); +// +//export arc4random +func libc_arc4random() uint32 diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index 0b513dca..fefba85f 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -120,3 +120,16 @@ func libc_getpagesize() int func syscall_Getpagesize() int { return libc_getpagesize() } + +func hardwareRand() (n uint64, ok bool) { + read := libc_getrandom(unsafe.Pointer(&n), 8, 0) + if read != 8 { + return 0, false + } + return n, true +} + +// ssize_t getrandom(void buf[.buflen], size_t buflen, unsigned int flags); +// +//export getrandom +func libc_getrandom(buf unsafe.Pointer, buflen uintptr, flags uint32) uint32 diff --git a/src/runtime/rand_hwrng.go b/src/runtime/rand_hwrng.go new file mode 100644 index 00000000..7154ffd7 --- /dev/null +++ b/src/runtime/rand_hwrng.go @@ -0,0 +1,16 @@ +//go:build baremetal && (nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3) + +// If you update the above build constraint, you'll probably also need to update +// src/crypto/rand/rand_baremetal.go. + +package runtime + +import "machine" + +func hardwareRand() (n uint64, ok bool) { + n1, err1 := machine.GetRNG() + n2, err2 := machine.GetRNG() + n = uint64(n1)<<32 | uint64(n2) + ok = err1 == nil && err2 == nil + return +} diff --git a/src/runtime/rand_norng.go b/src/runtime/rand_norng.go new file mode 100644 index 00000000..e6045008 --- /dev/null +++ b/src/runtime/rand_norng.go @@ -0,0 +1,7 @@ +//go:build baremetal && !(nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3) + +package runtime + +func hardwareRand() (n uint64, ok bool) { + return 0, false // no RNG available +} diff --git a/src/runtime/runtime_nintendoswitch.go b/src/runtime/runtime_nintendoswitch.go index 059e449d..10724e40 100644 --- a/src/runtime/runtime_nintendoswitch.go +++ b/src/runtime/runtime_nintendoswitch.go @@ -308,3 +308,8 @@ func svcOutputDebugString(str *uint8, size uint64) uint64 // //export svcGetInfo func svcGetInfo(output *uint64, id0 uint32, handle uint32, id1 uint64) uint64 + +func hardwareRand() (n uint64, ok bool) { + // TODO: see whether there is a RNG and use it. + return 0, false +} diff --git a/src/runtime/runtime_tinygowasm.go b/src/runtime/runtime_tinygowasm.go index bd8adea6..8ed73df7 100644 --- a/src/runtime/runtime_tinygowasm.go +++ b/src/runtime/runtime_tinygowasm.go @@ -85,3 +85,14 @@ func procPin() { //go:linkname procUnpin sync/atomic.runtime_procUnpin func procUnpin() { } + +func hardwareRand() (n uint64, ok bool) { + n |= uint64(libc_arc4random()) + n |= uint64(libc_arc4random()) << 32 + return n, true +} + +// uint32_t arc4random(void); +// +//export arc4random +func libc_arc4random() uint32 diff --git a/src/runtime/runtime_windows.go b/src/runtime/runtime_windows.go index 30cb00c1..97485294 100644 --- a/src/runtime/runtime_windows.go +++ b/src/runtime/runtime_windows.go @@ -228,3 +228,19 @@ func procPin() { //go:linkname procUnpin sync/atomic.runtime_procUnpin func procUnpin() { } + +func hardwareRand() (n uint64, ok bool) { + var n1, n2 uint32 + errCode1 := libc_rand_s(&n1) + errCode2 := libc_rand_s(&n2) + n = uint64(n1)<<32 | uint64(n2) + ok = errCode1 == 0 && errCode2 == 0 + return +} + +// Cryptographically secure random number generator. +// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rand-s?view=msvc-170 +// errno_t rand_s(unsigned int* randomValue); +// +//export rand_s +func libc_rand_s(randomValue *uint32) int32