summaryrefslogtreecommitdiff
path: root/util/nvmutil/lib
diff options
context:
space:
mode:
Diffstat (limited to 'util/nvmutil/lib')
-rw-r--r--util/nvmutil/lib/checksum.c12
-rw-r--r--util/nvmutil/lib/command.c18
-rw-r--r--util/nvmutil/lib/file.c167
-rw-r--r--util/nvmutil/lib/io.c67
-rw-r--r--util/nvmutil/lib/num.c288
-rw-r--r--util/nvmutil/lib/state.c16
-rw-r--r--util/nvmutil/lib/string.c22
-rw-r--r--util/nvmutil/lib/word.c12
8 files changed, 379 insertions, 223 deletions
diff --git a/util/nvmutil/lib/checksum.c b/util/nvmutil/lib/checksum.c
index 465b76bf..8565361b 100644
--- a/util/nvmutil/lib/checksum.c
+++ b/util/nvmutil/lib/checksum.c
@@ -1,10 +1,7 @@
/* SPDX-License-Identifier: MIT
- *
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
*
* Functions related to GbE NVM checksums.
- *
- * Related file: word.c
*/
#include <sys/types.h>
@@ -42,15 +39,14 @@ read_checksums(void)
if (cmd->arg_part)
_max_invalid = 1;
- /*
- * Skip verification on this part,
+ /* Skip verification on this part,
* but only when arg_part is set.
*/
_skip_part = f->part ^ 1;
for (_p = 0; _p < 2; _p++) {
- /*
- * Only verify a part if it was *read*
+
+ /* Only verify a part if it was *read*
*/
if (cmd->arg_part && (_p == _skip_part))
continue;
@@ -61,9 +57,11 @@ read_checksums(void)
}
if (_num_invalid >= _max_invalid) {
+
if (_max_invalid == 1)
err(ECANCELED, "%s: part %lu has a bad checksum",
f->fname, (unsigned long)f->part);
+
err(ECANCELED, "%s: No valid checksum found in file",
f->fname);
}
diff --git a/util/nvmutil/lib/command.c b/util/nvmutil/lib/command.c
index 367c949a..95e1b4f7 100644
--- a/util/nvmutil/lib/command.c
+++ b/util/nvmutil/lib/command.c
@@ -1,8 +1,5 @@
/* SPDX-License-Identifier: MIT
- *
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
- *
- * Command handlers for nvmutil
*/
#include <sys/types.h>
@@ -35,9 +32,6 @@ sanitize_command_list(void)
sanitize_command_index(c);
}
-/* TODO: specific config checks per command
- */
-
void
sanitize_command_index(unsigned long c)
{
@@ -142,7 +136,8 @@ set_cmd_args(int argc, char *argv[])
if (x->no_cmd)
usage();
- /* Maintainer bugs */
+ /* Maintainer bug
+ */
if (cmd->arg_part && argc < 4)
err(EINVAL,
"arg_part set for command that needs argc4");
@@ -172,7 +167,8 @@ conv_argv_part_num(const char *part_str)
if (part_str[0] == '\0' || part_str[1] != '\0')
err(EINVAL, "Partnum string '%s' wrong length", part_str);
- /* char signedness is implementation-defined */
+ /* char signedness is implementation-defined
+ */
ch = (unsigned char)part_str[0];
if (ch < '0' || ch > '1')
err(EINVAL, "Bad part number (%c)", ch);
@@ -286,16 +282,14 @@ set_mac_nib(unsigned long mac_str_pos,
err(EINVAL, "Invalid character '%c'",
mac->str[mac_str_pos + mac_nib_pos]);
- /*
- * If random, ensure that local/unicast bits are set.
+ /* If random, ensure that local/unicast bits are set.
*/
if ((mac_byte_pos == 0) && (mac_nib_pos == 1) &&
((mac_ch | 0x20) == 'x' ||
(mac_ch == '?')))
hex_num = (hex_num & 0xE) | 2; /* local, unicast */
- /*
- * MAC words stored big endian in-file, little-endian
+ /* MAC words stored big endian in-file, little-endian
* logically, so we reverse the order.
*/
mac->mac_buf[mac_byte_pos >> 1] |= hex_num <<
diff --git a/util/nvmutil/lib/file.c b/util/nvmutil/lib/file.c
index 406c4618..b4925ccd 100644
--- a/util/nvmutil/lib/file.c
+++ b/util/nvmutil/lib/file.c
@@ -1,8 +1,5 @@
/* SPDX-License-Identifier: MIT
- *
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
- *
- * Safe file handling.
*/
#include <sys/types.h>
@@ -51,6 +48,9 @@ err_same_file:
return -1;
}
+/* open() but with abort traps
+ */
+
void
xopen(int *fd_ptr, const char *path, int flags, struct stat *st)
{
@@ -67,8 +67,8 @@ xopen(int *fd_ptr, const char *path, int flags, struct stat *st)
err(errno, "%s: file not seekable", path);
}
-/* Ensure rename() is durable by syncing the
- * directory containing the target file.
+/* fsync() the directory of a file,
+ * useful for atomic writes
*/
int
@@ -177,28 +177,11 @@ err_fsync_dir:
return -1;
}
-/* create new tmpfile path
- *
- * ON SUCCESS:
- *
- * returns ptr to path string on success
- * ALSO: the int at *fd will be set,
- * indicating the file descriptor
- *
- * ON ERROR:
- *
- * return NULL (*fd not touched)
+/* returns ptr to path (string). if local>0:
+ * make tmpfile in the same directory as the
+ * file. if local==0, use TMPDIR
*
- * malloc() may set errno, but you should
- * not rely on errno from this function
- *
- * local: if non-zero, then only a file
- * name will be given, relative to
- * the current file name. for this,
- * the 3rd argument (path) must be non-null
- *
- * if local is zero, then 3rd arg (path)
- * is irrelevant and can be NULL
+ * if local==0, the 3rd argument is ignored
*/
char *
@@ -212,7 +195,7 @@ new_tmpfile(int *fd, int local, const char *path)
*/
char tmp_none[] = "";
char tmp_default[] = "/tmp";
- char default_tmpname[] = "tmpXXXXXX";
+ char default_tmpname[] = "tmpXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
char *tmpname;
char *base = NULL;
@@ -225,16 +208,6 @@ new_tmpfile(int *fd, int local, const char *path)
int fd_tmp = -1;
int flags;
- /*
- * 256 is the most
- * conservative path
- * size limit (posix),
- * but 4096 is modern
- *
- * set PATH_LEN as you
- * wish, at build time
- */
-
#if defined(PATH_LEN) && \
(PATH_LEN) >= 256
maxlen = PATH_LEN;
@@ -265,7 +238,7 @@ new_tmpfile(int *fd, int local, const char *path)
*/
tmpdir_len = xstrxlen(default_tmpname, maxlen);
} else {
- base = x_c_tmpdir();
+ base = get_tmpdir();
if (base == NULL)
base = tmp_default;
@@ -392,8 +365,12 @@ lock_file(int fd, int flags)
return 0;
}
+/* return TMPDIR, or fall back
+ * to portable defaults
+ */
+
char *
-x_c_tmpdir(void)
+get_tmpdir(void)
{
char *t;
struct stat st;
@@ -401,9 +378,12 @@ x_c_tmpdir(void)
t = getenv("TMPDIR");
if (t && *t) {
+
if (stat(t, &st) == 0 && S_ISDIR(st.st_mode)) {
+
if ((st.st_mode & S_IWOTH) && !(st.st_mode & S_ISVTX))
return NULL;
+
return t;
}
}
@@ -434,11 +414,11 @@ mkstemp_n(char *template)
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
unsigned long r;
- unsigned long max_len =
-#ifndef PATH_LEN
- 4096;
+#if defined(PATH_LEN) && \
+ (PATH_LEN) >= 256
+ unsigned long max_len = PATH_LEN;
#else
- (PATH_LEN);
+ unsigned long max_len = 4096;
#endif
len = xstrxlen(template, max_len);
@@ -579,45 +559,32 @@ err_rw_file_exact:
return -1;
}
-/* prw() - portable read-write
+/* prw() - portable read-write with more
+ * safety checks than barebones libc
*
- * This implements a portable analog of pwrite()
- * and pread() - note that this version is not
- * thread-safe (race conditions are possible on
- * shared file descriptors).
- *
- * This limitation is acceptable, since nvmutil is
- * single-threaded. Portability is the main goal.
+ * portable pwrite/pread on request, or real
+ * pwrite/pread libc functions can be used.
+ * the portable (non-libc) pread/pwrite is not
+ * thread-safe, because it does not prevent or
+ * mitigate race conditions on file descriptors
*
* If you need real pwrite/pread, just compile
* with flag: HAVE_REAL_PREAD_PWRITE=1
*
* A fallback is provided for regular read/write.
- * rw_type can be IO_READ, IO_WRITE, IO_PREAD
- * or IO_PWRITE
+ * rw_type can be IO_READ (read), IO_WRITE (write),
+ * IO_PREAD (pread) or IO_PWRITE
*
* loop_eagain does a retry loop on EAGAIN if set
* loop_eintr does a retry loop on EINTR if set
*
- * Unlike the bare syscalls, prw() does security
- * checks e.g. checks NULL strings, checks bounds,
- * also mitigates a few theoretical libc bugs.
- * It is designed for extremely safe single-threaded
- * I/O on applications that need it.
- *
- * NOTE: If you use loop_eagain (1), you enable wait
- * loop on EAGAIN. Beware if using this on a non-blocking
- * pipe (it could spin indefinitely).
+ * race conditions on non-libc pread/pwrite:
+ * if a file offset changes, abort, to mitage.
*
- * off_reset: if zero, and using fallback pwrite/pread
- * analogs, we check if a file offset changed,
- * which would indicate another thread changed
- * it, and return error, without resetting the
- * file - this would allow that thread to keep
- * running, but we could then cause a whole
- * program exit if we wanted to.
- * if not zero:
- * we reset and continue, and pray for the worst.
+ * off_reset 1: reset the file offset *once* if
+ * a change was detected, assuming
+ * nothing else is touching it now
+ * off_reset 0: never reset if changed
*/
long
@@ -644,16 +611,15 @@ prw(int fd, void *mem, unsigned long nrw,
r = -1;
- /* Programs like cat can use this,
- so we only check if it's a normal
- file if not looping EAGAIN */
+ /* do not use loop_eagain on
+ * normal files
+ */
+
if (!loop_eagain) {
- /*
- * Checking on every run of prw()
- * is expensive if called many
- * times, but is defensive in
- * case the status changes.
+ /* check whether the file
+ * changed
*/
+
if (check_file(fd, &st) == -1)
return -1;
}
@@ -703,39 +669,21 @@ real_pread_pwrite:
verified = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
loop_eagain, loop_eintr);
- /*
- * Partial thread-safety: detect
- * if the offset changed to what
- * we previously got. If it did,
- * then another thread may have
- * changed it. Enabled if
- * off_reset is OFF_RESET.
- *
- * We do this *once*, on the theory
- * that nothing is touching it now.
+ /* abort if the offset changed,
+ * indicating race condition. if
+ * off_reset enabled, reset *ONCE*
*/
+
if (off_reset && off != verified)
lseek_on_eintr(fd, off, SEEK_SET,
loop_eagain, loop_eintr);
do {
- /*
- * Verify again before I/O
- * (even with OFF_ERR)
- *
- * This implements the first check
- * even with OFF_ERR, but without
- * the recovery. On ERR_RESET, if
- * the check fails again, then we
- * know something else is touching
- * the file, so it's best that we
- * probably leave it alone and err.
- *
- * In other words, ERR_RESET only
- * tolerates one change. Any more
- * will cause an exit, including
- * per EINTR/EAGAIN re-spin.
+ /* check offset again, repeatedly.
+ * even if off_reset is set, this
+ * aborts if offsets change again
*/
+
verified = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
loop_eagain, loop_eintr);
@@ -831,9 +779,7 @@ err_is_file:
return -1;
}
-/* Check weirdness on buggy libc.
- *
- * POSIX can say whatever it wants.
+/* POSIX can say whatever it wants.
* specification != implementation
*/
@@ -888,11 +834,6 @@ err_rw_over_nrw:
#if !defined(HAVE_REAL_PREAD_PWRITE) || \
HAVE_REAL_PREAD_PWRITE < 1
-/*
- * lseek_on_eintr() does lseek() but optionally
- * on an EINTR/EAGAIN wait loop. Used by prw()
- * for setting offsets for positional I/O.
- */
off_t
lseek_on_eintr(int fd, off_t off, int whence,
int loop_eagain, int loop_eintr)
diff --git a/util/nvmutil/lib/io.c b/util/nvmutil/lib/io.c
index 06fd038f..5769dd05 100644
--- a/util/nvmutil/lib/io.c
+++ b/util/nvmutil/lib/io.c
@@ -1,5 +1,4 @@
/* SPDX-License-Identifier: MIT
- *
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
* I/O functions specific to nvmutil.
@@ -44,12 +43,11 @@ open_gbe_file(void)
if (_flags == -1)
err(errno, "%s: fcntl(F_GETFL)", f->fname);
- /*
- * O_APPEND must not be used, because this
- * allows POSIX write() to ignore the
- * current write offset and write at EOF,
- * which would therefore break pread/pwrite
+ /* O_APPEND allows POSIX write() to ignore
+ * the current write offset and write at EOF,
+ * which would break positional read/write
*/
+
if (_flags & O_APPEND)
err(EIO, "%s: O_APPEND flag", f->fname);
@@ -223,10 +221,10 @@ write_to_gbe_bin(void)
write_gbe_file();
- /*
- * We may otherwise read from
+ /* We may otherwise read from
* cache, so we must sync.
*/
+
if (fsync_on_eintr(f->tmp_fd) == -1)
err(errno, "%s: fsync (pre-verification)",
f->tname);
@@ -239,11 +237,6 @@ write_to_gbe_bin(void)
if (f->io_err_gbe)
err(EIO, "%s: bad write", f->fname);
- /*
- * success!
- * now just rename the tmpfile
- */
-
saved_errno = errno;
if (close_on_eintr(f->tmp_fd) == -1) {
@@ -258,6 +251,11 @@ write_to_gbe_bin(void)
errno = saved_errno;
+ /* tmpfile written, now we
+ * rename it back to the main file
+ * (we do atomic writes)
+ */
+
f->tmp_fd = -1;
f->gbe_fd = -1;
@@ -275,6 +273,7 @@ write_to_gbe_bin(void)
/* removed by rename
*/
+
if (f->tname != NULL)
free(f->tname);
@@ -338,17 +337,17 @@ check_written_part(unsigned long p)
f->rw_check_partial_read[p])
return;
- /*
- * We only load one part on-file, into memory but
+ /* We only load one part on-file, into memory but
* always at offset zero, for post-write checks.
- * That's why we hardcode good_checksum(0).
+ * That's why we hardcode good_checksum(0)
*/
+
buf_restore = f->buf;
- /*
- * good_checksum works on f->buf
+ /* good_checksum works on f->buf
* so let's change f->buf for now
*/
+
f->buf = f->pad;
if (good_checksum(0))
@@ -427,7 +426,8 @@ gbe_mv(void)
char *dest_tmp;
int dest_fd;
- /* will be set 0 if it doesn't */
+ /* will be set 0 if it doesn't
+ */
tmp_gbe_bin_exists = 1;
dest_tmp = NULL;
@@ -438,8 +438,8 @@ gbe_mv(void)
rval = rename(f->tname, f->fname);
if (rval > -1) {
- /*
- * same filesystem
+
+ /* rename on same filesystem
*/
tmp_gbe_bin_exists = 0;
@@ -455,19 +455,22 @@ gbe_mv(void)
if (errno != EXDEV)
goto ret_gbe_mv;
- /* cross-filesystem rename */
+ /*
+ * OR, cross-filesystem rename:
+ */
if ((rval = f->tmp_fd = open(f->tname,
O_RDONLY | O_BINARY)) == -1)
goto ret_gbe_mv;
- /* create replacement temp in target directory */
+ /* create replacement temp in target directory
+ */
dest_tmp = new_tmpfile(&dest_fd, 1, f->fname);
if (dest_tmp == NULL)
goto ret_gbe_mv;
- /* copy data */
-
+ /* copy data
+ */
rval = rw_file_exact(f->tmp_fd, f->bufcmp,
f->gbe_file_size, 0, IO_PREAD,
NO_LOOP_EAGAIN, LOOP_EINTR,
@@ -520,8 +523,7 @@ ret_gbe_mv:
f->tmp_fd = -1;
}
- /*
- * before this function is called,
+ /* before this function is called,
* tmp_fd may have been moved
*/
if (tmp_gbe_bin_exists) {
@@ -532,8 +534,7 @@ ret_gbe_mv:
}
if (rval < 0) {
- /*
- * if nothing set errno,
+ /* if nothing set errno,
* we assume EIO, or we
* use what was set
*/
@@ -546,8 +547,7 @@ ret_gbe_mv:
return rval;
}
-/*
- * This one is similar to gbe_file_offset,
+/* This one is similar to gbe_file_offset,
* but used to check Gbe bounds in memory,
* and it is *also* used during file I/O.
*/
@@ -566,12 +566,9 @@ gbe_mem_offset(unsigned long p, const char *f_op)
(f->buf + (unsigned long)gbe_off);
}
-/*
- * I/O operations filtered here. These operations must
+/* I/O operations filtered here. These operations must
* only write from the 0th position or the half position
* within the GbE file, and write 4KB of data.
- *
- * This check is called, to ensure just that.
*/
off_t
gbe_file_offset(unsigned long p, const char *f_op)
diff --git a/util/nvmutil/lib/num.c b/util/nvmutil/lib/num.c
index 0442b86c..bbb5a83e 100644
--- a/util/nvmutil/lib/num.c
+++ b/util/nvmutil/lib/num.c
@@ -1,5 +1,4 @@
/* SPDX-License-Identifier: MIT
- *
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
* Numerical functions.
@@ -9,6 +8,10 @@
#include <sys/param.h>
#endif
#include <sys/types.h>
+#if defined(FALLBACK_RAND_1989) && \
+ (FALLBACK_RAND_1989) > 0
+#include <sys/time.h>
+#endif
#include <errno.h>
#if !((defined(__OpenBSD__) && (OpenBSD) >= 201) || \
@@ -18,6 +21,11 @@
#endif
#include <limits.h>
#include <stddef.h>
+#include <string.h>
+#if defined(FALLBACK_RAND_1989) && \
+ (FALLBACK_RAND_1989) > 0
+#include <time.h>
+#endif
#include <unistd.h>
#include "../include/common.h"
@@ -45,9 +53,12 @@ hextonum(char ch_s)
/* Random numbers
*/
+
unsigned long
rlong(void)
{
+#if !(defined(FALLBACK_RAND_1989) && \
+ ((FALLBACK_RAND_1989) > 0))
#if (defined(__OpenBSD__) && (OpenBSD) >= 201) || \
defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__APPLE__)
@@ -57,39 +68,278 @@ 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;
+ int retries = 0;
+ int max_retries = 100;
- unsigned long rval;
+#if defined(__linux__)
+#if defined(HAVE_GETRANDOM) || \
+ defined(HAVE_GETRANDOM_SYSCALL)
- fd = open("/dev/urandom", O_RDONLY | O_BINARY);
+ /* 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
+ */
-#ifdef __OpenBSD__
- if (fd < 0) /* old openbsd */
- fd = open("/dev/arandom", O_RDONLY | O_BINARY);
+ 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
+ * 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 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 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);
+#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 (fd < 0)
- err(errno, "can't open random device");
+ if (new_nr < 0 || new_nr < (long)sizeof(rbuf))
+ goto retry_urandom_read;
- nr = rw_file_exact(fd, (unsigned char *)&rval,
- sizeof(unsigned long), 0, IO_READ, LOOP_EAGAIN,
- LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR);
+ /* only reset buffer after successful refill */
+ nr = new_nr;
+ off = 0;
- if (close_on_eintr(fd) < 0)
- err(errno, "Can't close randomness fd");
+ /* to mitigate file descriptor
+ * injection, we do not re-use
+ * the same descriptor each time
+ */
+ (void) close_on_eintr(fd);
+ fd = -1;
+ }
- if (nr != sizeof(unsigned long))
- err(errno, "Incomplete read from random device");
+ fd = -1;
+ retries = 0;
+
+ memcpy(&rval, rbuf + off, sizeof(unsigned long));
+
+ nr -= (long)sizeof(unsigned long);
+ off += sizeof(unsigned long);
return rval;
+
+rlong_next:
+
+ fd = -1;
+ off = 0;
+ nr = -1;
+
+ err(EIO, "Can't read from /dev/[ua]random");
+ return 0;
+
+#endif
+#else /* FALLBACK_RAND_1989 */
+ /* your computer is from a museum
+ */
+ unsigned long mix = 0;
+ int nr;
+
+ /* 100 times, for entropy
+ */
+ for (nr = 0; nr < 100; nr++)
+ mix ^= fallback_rand_1989();
+
+ /* 101 times ;)
+ */
+ return fallback_rand_1989();
#endif
}
+#if !(defined(FALLBACK_RAND_1989) && \
+ ((FALLBACK_RAND_1989) > 0))
+#if defined(__linux__)
+#if defined(HAVE_GETRANDOM) || \
+ defined(HAVE_GETRANDOM_SYSCALL)
+int
+fallback_rand_getrandom(void *buf, unsigned long len)
+{
+ unsigned long off = 0;
+ long rval = -1;
+
+ if (!len)
+ return -1;
+
+ if (buf == NULL)
+ return -1;
+
+#if defined(HAVE_GETRANDOM) || \
+ defined(HAVE_GETRANDOM_SYSCALL)
+
+ while (off < len) {
+
+#if defined(HAVE_GETRANDOM)
+ rval = (long)getrandom((char *)buf + off, len - off, 0);
+#elif defined(HAVE_GETRANDOM_SYSCALL)
+ rval = (long)syscall(SYS_getrandom,
+ (char *)buf + off, len - off, 0);
+#endif
+
+ if (rval < 0) {
+ if (errno == EINTR)
+ continue;
+
+ return -1; /* unsupported by kernel */
+ }
+
+ off += (unsigned long)rval;
+ }
+
+ return 0;
+
+#else
+ (void)buf;
+ (void)len;
+
+ return -1;
+#endif
+}
+#endif
+#endif
+#else
+/* nobody should use this
+ * (not crypto-safe)
+ */
+unsigned long
+fallback_rand_1989(void)
+{
+ static unsigned long mix = 0;
+ static unsigned long counter = 0;
+
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ mix ^= (unsigned long)tv.tv_sec
+ ^ (unsigned long)tv.tv_usec
+ ^ (unsigned long)getpid()
+ ^ (unsigned long)&mix
+ ^ counter++
+ ^ entropy_jitter();
+
+ /*
+ * Stack addresses can vary between
+ * calls, thus increasing entropy.
+ */
+ mix ^= (unsigned long)&mix;
+ mix ^= (unsigned long)&tv;
+ mix ^= (unsigned long)&counter;
+
+ return mix;
+}
+
+unsigned long
+entropy_jitter(void)
+{
+ unsigned long mix;
+
+ struct timeval a, b;
+ long mix_diff;
+
+ int c;
+
+ mix = 0;
+
+ gettimeofday(&a, NULL);
+
+ for (c = 0; c < 32; c++) {
+
+ getpid();
+ gettimeofday(&b, NULL);
+
+ /*
+ * prevent negative numbers to prevent overflow,
+ * which would bias rand to large numbers
+ */
+ mix_diff = (long)(b.tv_usec - a.tv_usec);
+ if (mix_diff < 0)
+ mix_diff = -mix_diff;
+
+ mix ^= (unsigned long)(mix_diff);
+
+ mix ^= (unsigned long)&mix;
+
+ }
+
+ return mix;
+}
+#endif
+
void
check_bin(unsigned long a, const char *a_name)
{
diff --git a/util/nvmutil/lib/state.c b/util/nvmutil/lib/state.c
index 946b8919..02a3e51c 100644
--- a/util/nvmutil/lib/state.c
+++ b/util/nvmutil/lib/state.c
@@ -1,9 +1,7 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
*
- * This tool lets you modify Intel GbE NVM (Gigabit Ethernet
- * Non-Volatile Memory) images, e.g. change the MAC address.
- * These images configure your Intel Gigabit Ethernet adapter.
+ * State machine (singleton) for nvmutil data.
*/
#ifdef __OpenBSD__
@@ -24,12 +22,6 @@
#include "../include/common.h"
-/*
- * Initialise program state,
- * load GbE file and verify
- * data, ready for operation
- * (singleton design)
- */
struct xstate *
xstatus(int argc, char *argv[])
{
@@ -147,8 +139,8 @@ xstatus(int argc, char *argv[])
#if defined(__OpenBSD__) && defined(OpenBSD) && \
OpenBSD >= 604
- if (unveil(f->tname, "rwc") == -1)
- err(errno, "unveil rwc: %s", f->tname);
+ if (unveil(us.f.tname, "rwc") == -1)
+ err(errno, "unveil rwc: %s", us.f.tname);
#endif
if (fstat(us.f.tmp_fd, &us.f.tmp_st) < 0)
err(errno, "%s: stat", us.f.tname);
@@ -166,7 +158,7 @@ xstatus(int argc, char *argv[])
err(errno, "%s: unveil r", us.f.fname);
} else {
if (unveil(us.f.fname, "rwc") == -1)
- err(errno, "%s: unveil rw", us.f.tname);
+ err(errno, "%s: unveil rw", us.f.fname);
}
if (unveil(us.f.tname, "rwc") == -1)
diff --git a/util/nvmutil/lib/string.c b/util/nvmutil/lib/string.c
index 4139c354..b1a5c3e2 100644
--- a/util/nvmutil/lib/string.c
+++ b/util/nvmutil/lib/string.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
- * String handling.
+ * String functions
*/
#include <sys/types.h>
@@ -13,10 +13,10 @@
#include "../include/common.h"
-/*
- * Portable strcmp() but blocks NULL/empty/unterminated
- * strings. Even stricter than strncmp().
+/* Portable strncmp() that blocks
+ * NULL/empty/unterminated strings
*/
+
int
xstrxcmp(const char *a, const char *b, unsigned long maxlen)
{
@@ -43,24 +43,16 @@ xstrxcmp(const char *a, const char *b, unsigned long maxlen)
return ac - bc;
}
- /*
- * We reached maxlen, so assume unterminated string.
- */
err(EINVAL, "Unterminated string in xstrxcmp");
- /*
- * Should never reach here. This keeps compilers happy.
- */
errno = EINVAL;
return -1;
}
-/*
- * strnlen() but aborts on NULL input, and empty strings.
- * Our version also prohibits unterminated strings.
- * strnlen() was standardized in POSIX.1-2008 and is not
- * available on some older systems, so we provide our own.
+/* Portable strncmp() that blocks
+ * NULL/empty/unterminated strings
*/
+
unsigned long
xstrxlen(const char *scmp, unsigned long maxlen)
{
diff --git a/util/nvmutil/lib/word.c b/util/nvmutil/lib/word.c
index 6fd5974c..5d9220c7 100644
--- a/util/nvmutil/lib/word.c
+++ b/util/nvmutil/lib/word.c
@@ -1,9 +1,8 @@
/* SPDX-License-Identifier: MIT
- *
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
*
- * Manipulate sixteen-bit little-endian
- * words on Intel GbE NVM configurations.
+ * Manipulate Intel GbE NVM words, which are 16-bit little
+ * endian in the files (MAC address words are big endian).
*/
#include <sys/types.h>
@@ -13,13 +12,6 @@
#include "../include/common.h"
-/*
- * GbE NVM files store 16-bit (2-byte) little-endian words.
- * We must therefore swap the order when reading or writing.
- *
- * NOTE: The MAC address words are stored big-endian in-file.
- */
-
unsigned short
nvm_word(unsigned long pos16, unsigned long p)
{