util/nvmutil: rand: use getrandom on newer linux

we still fall back to the old /dev/urandom read
on older linux, via runtime detection (ENOSYS).

getrandom is better, because it guarantees entropy
via blocking, and works even when /dev/urandom
is unavailable.

it has the same practical benefit as arc4random,
which i use on bsd. linux can have arc4random,
but not every linux libc has it, so it's better
to use getrandom on linux.

older linux will fall back to /dev/urandom

Signed-off-by: Leah Rowe <leah@libreboot.org>
This commit is contained in:
Leah Rowe
2026-03-19 18:34:28 +00:00
parent 7c66a788bd
commit f8b07dba29
2 changed files with 105 additions and 3 deletions

View File

@@ -9,6 +9,25 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <limits.h> #include <limits.h>
/* for linux getrandom
*/
#if defined(__linux__)
#include <errno.h>
#if defined(__has_include)
#if __has_include(<sys/random.h>)
#include <sys/random.h>
#define HAVE_GETRANDOM 1
#endif
#endif
#if !defined(HAVE_GETRANDOM)
#include <sys/syscall.h>
#if defined(SYS_getrandom)
#define HAVE_GETRANDOM_SYSCALL 1
#endif
#endif
#endif
#define items(x) (sizeof((x)) / sizeof((x)[0])) #define items(x) (sizeof((x)) / sizeof((x)[0]))
/* system prototypes /* system prototypes
@@ -318,6 +337,12 @@ void set_mac_nib(unsigned long mac_str_pos,
unsigned long mac_byte_pos, unsigned long mac_nib_pos); unsigned long mac_byte_pos, unsigned long mac_nib_pos);
unsigned short hextonum(char ch_s); unsigned short hextonum(char ch_s);
unsigned long rlong(void); unsigned long rlong(void);
#if defined(__linux__)
#if defined(HAVE_GETRANDOM) || \
defined(HAVE_GETRANDOM_SYSCALL)
int fallback_rand_getrandom(void *buf, size_t len);
#endif
#endif
void write_mac_part(unsigned long partnum); void write_mac_part(unsigned long partnum);
/* Helper functions for command: dump /* Helper functions for command: dump

View File

@@ -75,6 +75,37 @@ rlong(void)
unsigned long rval; unsigned long rval;
long new_nr; long new_nr;
int retries = 0;
int max_retries = 100;
#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
*
* similar benefits to arc4random
* e.g. works in chroot, blocks
* until it has enough entropy,
* and works even when /dev/urandom
* is available (doesn't use it);
* it's generally more reliable
*/
if (fallback_rand_getrandom(&rval, sizeof(rval)) == 0)
return rval;
/*
* now fall back to urandom if getrandom failed:
*/
#endif
#endif
/* reading from urandom is inherently /* reading from urandom is inherently
* unreliable on old systems, even if * unreliable on old systems, even if
* newer systems make it more reliable * newer systems make it more reliable
@@ -87,9 +118,6 @@ rlong(void)
* tricks to mitigate possible os bugs * tricks to mitigate possible os bugs
*/ */
int retries = 0;
int max_retries = 100;
retry_urandom_read: retry_urandom_read:
if (++retries > max_retries) if (++retries > max_retries)
@@ -104,6 +132,10 @@ retry_urandom_read:
O_CLOEXEC); O_CLOEXEC);
#ifdef __OpenBSD__ #ifdef __OpenBSD__
/* TODO: dead code
openbsd 2.1 had arc4random.
arandam was introduced **later**
*/
if (fd < 0) /* old openbsd */ if (fd < 0) /* old openbsd */
fd = open("/dev/arandom", fd = open("/dev/arandom",
O_RDONLY | O_BINARY | O_NOFOLLOW | O_RDONLY | O_BINARY | O_NOFOLLOW |
@@ -178,6 +210,51 @@ rlong_next:
#endif #endif
} }
#if defined(__linux__)
#if defined(HAVE_GETRANDOM) || \
defined(HAVE_GETRANDOM_SYSCALL)
int
fallback_rand_getrandom(void *buf, size_t len)
{
ssize_t rval = -1;
/* keep strict compiler
* happy if unused
*/
(void)rval;
(void)buf;
(void)len;
#if defined(HAVE_GETRANDOM)
do {
rval = getrandom(buf, len, 0);
} while (rval < 0 && errno == EINTR);
#elif defined(HAVE_GETRANDOM_SYSCALL)
do {
rval = syscall(SYS_getrandom, buf, len, 0);
} while (ret < 0 && errno == EINTR);
#else
return -1;
#endif
#if defined(HAVE_GETRANDOM) || \
defined(HAVE_GETRANDOM_SYSCALL)
if (rval == (ssize_t)len) {
return 0;
}
if (rval < 0 && errno == ENOSYS)
return -1; /* not supported by kernel */
return -1;
#endif
}
#endif
#endif
void void
check_bin(unsigned long a, const char *a_name) check_bin(unsigned long a, const char *a_name)
{ {