From 7bff5712b4cf528b0b00649538545e66cc3a39ee Mon Sep 17 00:00:00 2001 From: Leah Rowe Date: Wed, 25 Mar 2026 11:03:23 +0000 Subject: [PATCH] util/libreboot-utils: simplified rand only use the getrandom syscall on linux, or arc4random. the /dev/urandom fallback is removed, and we use the syscall; failure is almost certainly unlikely, but if it fails, we abort. this provides therefore the same guarantee as bsd arc4random, since it will never return under fault conditions. it will only ever return success, or abort. nobody should be using /dev/urandom in 2026. Signed-off-by: Leah Rowe --- util/libreboot-utils/include/common.h | 19 +- util/libreboot-utils/lib/rand.c | 242 +++++--------------------- 2 files changed, 46 insertions(+), 215 deletions(-) diff --git a/util/libreboot-utils/include/common.h b/util/libreboot-utils/include/common.h index 0bab30de..fb3aa886 100644 --- a/util/libreboot-utils/include/common.h +++ b/util/libreboot-utils/include/common.h @@ -13,24 +13,13 @@ #include #include #include +#include /* for linux getrandom */ #if defined(__linux__) -#include -#if defined(__has_include) -#if __has_include() #include -#define HAVE_GETRANDOM 1 -#endif -#endif -#if !defined(HAVE_GETRANDOM) #include -#if !defined(SYS_getrandom) -#define HAVE_GETRANDOM_SYSCALL 1 -#endif -#endif - #endif #define items(x) (sizeof((x)) / sizeof((x)[0])) @@ -396,12 +385,6 @@ int dcat(const char *s, size_t n, unsigned short hextonum(char ch_s); size_t rlong(void); -#if defined(__linux__) -#if defined(HAVE_GETRANDOM) || \ - defined(HAVE_GETRANDOM_SYSCALL) -int fallback_rand_getrandom(void *buf, size_t len); -#endif -#endif /* Helper functions for command: dump */ diff --git a/util/libreboot-utils/lib/rand.c b/util/libreboot-utils/lib/rand.c index 900c3ed3..ecf07142 100644 --- a/util/libreboot-utils/lib/rand.c +++ b/util/libreboot-utils/lib/rand.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "../include/common.h" @@ -43,230 +44,77 @@ * theoretically. */ +/* for the linux version: we use only the + * syscall, because we cannot trust /dev/urandom + * to be as robust, and some libc implementations + * may default to /dev/urandom under fault conditions. + * + * for general high reliability, we must abort on + * failure. in practise, it will likely never fail. + * the arc4random call on bsd never returns error. + */ + size_t rlong(void) { + size_t rval; + int saved_errno = errno; + #if (defined(__OpenBSD__) && (OpenBSD) >= 201) || \ defined(__FreeBSD__) || \ defined(__NetBSD__) || defined(__APPLE__) - int saved_errno = errno; - size_t rval; - arc4random_buf(&rval, sizeof(size_t)); + goto out; - errno = saved_errno; - return rval; -#else - static int fd = -1; - static ssize_t nr = -1; - static size_t off = 0; -#if defined (BUFSIZ) - static char rbuf[BUFSIZ]; -#else -#ifndef PORTABLE - static char rbuf[4096]; -#elif ((PORTABLE) > 0) - static char rbuf[256]; /* scarce memory on old systems */ -#else - static char rbuf[4096]; /* typical 32-bit BUFSIZ */ -#endif -#endif - size_t rval; - ssize_t new_nr; +#elif defined(__linux__) - int retries = 0; - int max_retries = 100; - - int saved_errno = errno; - -#if defined(__linux__) -#if defined(HAVE_GETRANDOM) || \ - defined(HAVE_GETRANDOM_SYSCALL) - - /* linux getrandom() - * - * we *can* use arc4random on - * modern linux, but not on - * every libc. better use the - * official linux function - */ - - if (fallback_rand_getrandom(&rval, sizeof(rval)) == 0) { - errno = saved_errno; - return rval; - } - - /* - * now fall back to urandom if getrandom failed: - */ -#endif -#endif - - /* reading from urandom is inherently - * unreliable on old systems, even if - * newer systems make it more reliable - * - * modern linux/bsd make it safe, but - * we have to assume that someone is - * compiling this on linux from 1999 - * - * this logic therefore applies various - * tricks to mitigate possible os bugs - */ - -retry_urandom_read: - - if (++retries > max_retries) - goto err_rlong; - - if (nr < 0 || nr < (ssize_t)sizeof(size_t)) { - - if (fd < 0) { - - fd = open("/dev/urandom", - O_RDONLY | O_BINARY | O_NOFOLLOW | - O_CLOEXEC | O_NOCTTY); - -#ifdef USE_OLD_DEV_RANDOM -#if (USE_OLD_DEV_RANDOM) > 0 - /* WARNING: - * /dev/random may block - * forever and does **NOT** - * guarantee better entropy - * on old systems - * - * only use it if needed - */ - - if (fd < 0) - fd = open("/dev/random", - O_RDONLY | O_BINARY | O_NOFOLLOW | - O_CLOEXEC | O_NOCTTY); -#endif -#endif - - if (fd < 0) - goto retry_urandom_read; - - retries = 0; - } - - new_nr = rw_file_exact(fd, (unsigned char *)rbuf, - sizeof(rbuf), 0, IO_READ, LOOP_EAGAIN, - LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR); - - if (new_nr < 0 || new_nr < (ssize_t)sizeof(rbuf)) - goto retry_urandom_read; - - /* only reset buffer after successful refill */ - nr = new_nr; - off = 0; - - /* don't re-use same fd, to mitaget - * fd injection */ - close_no_err(&fd); - } - - fd = -1; - retries = 0; - - memcpy(&rval, rbuf + off, sizeof(size_t)); - - nr -= (ssize_t)sizeof(size_t); - off += sizeof(size_t); - - errno = saved_errno; - - return rval; - -err_rlong: - - if (errno == saved_errno) - errno = EIO; - - return 0; - -#endif -#else /* FALLBACK_RAND_1989 */ - /* your computer is from a museum - */ - size_t mix = 0; - int nr = 0; - int saved_errno = errno; - - /* 100 times, for entropy - */ - for (nr = 0; nr < 100; nr++) - mix ^= fallback_rand_1989(); - - errno = saved_errno; - return mix; -#endif -} - -#if !(defined(FALLBACK_RAND_1989) && \ - ((FALLBACK_RAND_1989) > 0)) -#if defined(__linux__) -#if defined(HAVE_GETRANDOM) || \ - defined(HAVE_GETRANDOM_SYSCALL) -int /* yes, linux is a fallback */ -fallback_rand_getrandom(void *buf, size_t len) -{ size_t off = 0; - ssize_t rval = -1; - int saved_errno = errno; + size_t len = sizeof(rval); - if (!len) { - errno = EINVAL; - return -1; - } + ssize_t rc; - if (buf == NULL) { - errno = EFAULT; - return -1; - } - -#if defined(HAVE_GETRANDOM) || \ - defined(HAVE_GETRANDOM_SYSCALL) + if (!len) + goto err; while (off < len) { -#if defined(HAVE_GETRANDOM) - rval = (ssize_t)getrandom((char *)buf + off, len - off, 0); -#elif defined(HAVE_GETRANDOM_SYSCALL) - rval = (ssize_t)syscall(SYS_getrandom, - (char *)buf + off, len - off, 0); -#endif + rc = (ssize_t)syscall(SYS_getrandom, + (char *)&rval + off, len - off, 0); - if (rval < 0) { + if (rc < 0) { if (errno == EINTR || errno == EAGAIN) continue; - errno = EIO; - return -1; /* unsupported by kernel */ + goto err; /* possibly unsupported by kernel */ } - if (rval == 0) { - errno = EIO; - return -1; - } + if (rval == 0) + goto err; - off += (size_t)rval; + off += (size_t)rc; } - errno = saved_errno; - return 0; + goto out; + + return rval; +err: + /* + * getrandom can return with error, butt arc4random + * doesn't. generally, getrandom will be reliably, + * but we of course have to maintain parity with + * BSD. So a rand failure is to be interpreted as + * a major systems failure, and we act accordingly. + */ + err_no_cleanup(1, ECANCELED, "Randomisation failure"); + exit(1); #else - (void)buf; - (void)len; - - errno = EIO; - return -1; +#error Unsupported operating system (possibly unsecure randomisation) #endif + +out: + errno = saved_errno; + return rval; } #endif -#endif - -#endif