diff options
| author | Leah Rowe <leah@libreboot.org> | 2026-03-19 17:27:02 +0000 |
|---|---|---|
| committer | Leah Rowe <leah@libreboot.org> | 2026-03-19 18:04:33 +0000 |
| commit | 7c66a788bdfc8136a603eec6d805b75cc382b239 (patch) | |
| tree | 1c1c7dc8fd57913e92b517e90c8d6bb20d63ec49 /util/nvmutil/lib/num.c | |
| parent | 55f006318a35990d7d19a796e9af4c5f351b389a (diff) | |
util/nvmutil: buffered urandom reads
also generally tidied the code and made
it more robust e.g. retries
Signed-off-by: Leah Rowe <leah@libreboot.org>
Diffstat (limited to 'util/nvmutil/lib/num.c')
| -rw-r--r-- | util/nvmutil/lib/num.c | 122 |
1 files changed, 105 insertions, 17 deletions
diff --git a/util/nvmutil/lib/num.c b/util/nvmutil/lib/num.c index 62a3b286..48c8dee1 100644 --- a/util/nvmutil/lib/num.c +++ b/util/nvmutil/lib/num.c @@ -17,6 +17,7 @@ #endif #include <limits.h> #include <stddef.h> +#include <string.h> #include <unistd.h> #include "../include/common.h" @@ -57,36 +58,123 @@ rlong(void) return rval; #else - int fd; + static int fd = -1; + static long nr = -1; + static unsigned long 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 + unsigned long rval; + long new_nr; - long nr; + /* 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 + */ - unsigned long rval; + int retries = 0; + int max_retries = 100; - fd = open("/dev/urandom", O_RDONLY | O_BINARY); +retry_urandom_read: + + if (++retries > max_retries) + goto rlong_next; + + if (nr < 0 || nr < (long)sizeof(unsigned long)) { + + if (fd < 0) { + + fd = open("/dev/urandom", + O_RDONLY | O_BINARY | O_NOFOLLOW | + O_CLOEXEC); #ifdef __OpenBSD__ - if (fd < 0) /* old openbsd */ - fd = open("/dev/arandom", O_RDONLY | O_BINARY); + if (fd < 0) /* old openbsd */ + fd = open("/dev/arandom", + O_RDONLY | O_BINARY | O_NOFOLLOW | + O_CLOEXEC); +#endif + +#ifdef PORTABLE_RAND_DEV +#if (PORTABLE_RAND_DEV) > 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); +#endif #endif - if (fd < 0) - fd = open("/dev/random", O_RDONLY | O_BINARY); + 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 < (long)sizeof(rbuf)) + goto retry_urandom_read; + + /* only reset buffer after successful refill */ + nr = new_nr; + off = 0; - if (fd < 0) - err(errno, "can't open random device"); + /* to mitigate file descriptor + * injection, we do not re-use + * the same descriptor each time + */ + (void) close_on_eintr(fd); + fd = -1; + } - nr = rw_file_exact(fd, (unsigned char *)&rval, - sizeof(unsigned long), 0, IO_READ, LOOP_EAGAIN, - LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR); + fd = -1; + retries = 0; - if (close_on_eintr(fd) < 0) - err(errno, "Can't close randomness fd"); + memcpy(&rval, rbuf + off, sizeof(unsigned long)); - if (nr != sizeof(unsigned long)) - err(errno, "Incomplete read from random device"); + nr -= (long)sizeof(unsigned long); + off += sizeof(unsigned long); return rval; + +rlong_next: + + fd = -1; + off = 0; + nr = -1; + +/* TODO: will re-add timer-based fallback here #if defined(PORTABLE) */ + + err(EIO, "Can't read from /dev/[ua]random"); + return 0; + + /* add portable timers here */ #endif } |
