diff options
| author | Leah Rowe <leah@libreboot.org> | 2026-03-24 00:28:15 +0000 |
|---|---|---|
| committer | Leah Rowe <leah@libreboot.org> | 2026-03-24 01:25:53 +0000 |
| commit | f2544d094ba88e1cfbb7993ad67444852cfd5efd (patch) | |
| tree | 252172594a1284e59e88c73c22f0201508809022 /util/nvmutil/lib | |
| parent | afcd535816b45fd7b0e0d07c1a8580f6f462f5e4 (diff) | |
util/mkhtemp: new utility (hardened mktemp)
part of the same code library as nvmutil.
as part of this, i renamed util/nvmutil
to util/libreboot-utils/ because it is
now a multi-utility codebase.
this is more efficient, since i also wish
to use mkhtemp (function) in nvmutil.
Signed-off-by: Leah Rowe <leah@libreboot.org>
Diffstat (limited to 'util/nvmutil/lib')
| -rw-r--r-- | util/nvmutil/lib/checksum.c | 108 | ||||
| -rw-r--r-- | util/nvmutil/lib/command.c | 563 | ||||
| -rw-r--r-- | util/nvmutil/lib/file.c | 1136 | ||||
| -rw-r--r-- | util/nvmutil/lib/io.c | 673 | ||||
| -rw-r--r-- | util/nvmutil/lib/mkhtemp.c | 1133 | ||||
| -rw-r--r-- | util/nvmutil/lib/num.c | 444 | ||||
| -rw-r--r-- | util/nvmutil/lib/state.c | 279 | ||||
| -rw-r--r-- | util/nvmutil/lib/string.c | 114 | ||||
| -rw-r--r-- | util/nvmutil/lib/usage.c | 30 | ||||
| -rw-r--r-- | util/nvmutil/lib/word.c | 68 |
10 files changed, 0 insertions, 4548 deletions
diff --git a/util/nvmutil/lib/checksum.c b/util/nvmutil/lib/checksum.c deleted file mode 100644 index b417dc7e..00000000 --- a/util/nvmutil/lib/checksum.c +++ /dev/null @@ -1,108 +0,0 @@ -/* SPDX-License-Identifier: MIT - * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org> - * - * Functions related to GbE NVM checksums. - */ - -#include <sys/types.h> -#include <sys/stat.h> - -#include <errno.h> -#include <limits.h> -#include <stddef.h> -#include <stdlib.h> - -#include "../include/common.h" - -void -read_checksums(void) -{ - struct xstate *x = xstatus(); - struct commands *cmd = &x->cmd[x->i]; - struct xfile *f = &x->f; - - size_t _p; - size_t _skip_part; - - unsigned char _num_invalid; - unsigned char _max_invalid; - - f->part_valid[0] = 0; - f->part_valid[1] = 0; - - if (!cmd->chksum_read) - return; - - _num_invalid = 0; - _max_invalid = 2; - - if (cmd->arg_part) - _max_invalid = 1; - - /* 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* - */ - if (cmd->arg_part && (_p == _skip_part)) - continue; - - f->part_valid[_p] = good_checksum(_p); - if (!f->part_valid[_p]) - ++_num_invalid; - } - - if (_num_invalid >= _max_invalid) { - - if (_max_invalid == 1) - err(ECANCELED, "%s: part %lu has a bad checksum", - f->fname, (size_t)f->part); - - err(ECANCELED, "%s: No valid checksum found in file", - f->fname); - } -} - -int -good_checksum(size_t partnum) -{ - unsigned short expected_checksum; - unsigned short actual_checksum; - - expected_checksum = - calculated_checksum(partnum); - - actual_checksum = - nvm_word(NVM_CHECKSUM_WORD, partnum); - - if (expected_checksum == actual_checksum) { - return 1; - } else { - return 0; - } -} - -void -set_checksum(size_t p) -{ - check_bin(p, "part number"); - set_nvm_word(NVM_CHECKSUM_WORD, p, calculated_checksum(p)); -} - -unsigned short -calculated_checksum(size_t p) -{ - size_t c; - unsigned int val16; - - val16 = 0; - - for (c = 0; c < NVM_CHECKSUM_WORD; c++) - val16 += (unsigned int)nvm_word(c, p); - - return (unsigned short)((NVM_CHECKSUM - val16) & 0xffff); -} diff --git a/util/nvmutil/lib/command.c b/util/nvmutil/lib/command.c deleted file mode 100644 index 3a863d23..00000000 --- a/util/nvmutil/lib/command.c +++ /dev/null @@ -1,563 +0,0 @@ -/* SPDX-License-Identifier: MIT - * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org> - */ - -#include <sys/types.h> -#include <sys/stat.h> - -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <stdio.h> -#include <stddef.h> -#include <string.h> -#include <unistd.h> - -#include "../include/common.h" - -/* Guard against regressions by maintainers (command table) - */ - -void -sanitize_command_list(void) -{ - struct xstate *x = xstatus(); - - size_t c; - size_t num_commands; - - num_commands = items(x->cmd); - - for (c = 0; c < num_commands; c++) - sanitize_command_index(c); -} - -void -sanitize_command_index(size_t c) -{ - struct xstate *x = xstatus(); - struct commands *cmd = &x->cmd[c]; - - int _flag; - size_t gbe_rw_size; - - size_t rval; - - check_command_num(c); - - if (cmd->argc < 3) - err(EINVAL, "cmd index %lu: argc below 3, %d", - (size_t)c, cmd->argc); - - if (cmd->str == NULL) - err(EINVAL, "cmd index %lu: NULL str", - (size_t)c); - - if (*cmd->str == '\0') - err(EINVAL, "cmd index %lu: empty str", - (size_t)c); - - if (slen(cmd->str, MAX_CMD_LEN +1, &rval) < 0) - err(errno, "Could not get command length"); - - if (rval > MAX_CMD_LEN) { - err(EINVAL, "cmd index %lu: str too long: %s", - (size_t)c, cmd->str); - } - - if (cmd->run == NULL) - err(EINVAL, "cmd index %lu: cmd ptr null", - (size_t)c); - - check_bin(cmd->arg_part, "cmd.arg_part"); - check_bin(cmd->chksum_read, "cmd.chksum_read"); - check_bin(cmd->chksum_write, "cmd.chksum_write"); - - gbe_rw_size = cmd->rw_size; - - switch (gbe_rw_size) { - case GBE_PART_SIZE: - case NVM_SIZE: - break; - default: - err(EINVAL, "Unsupported rw_size: %lu", - (size_t)gbe_rw_size); - } - - if (gbe_rw_size > GBE_PART_SIZE) - err(EINVAL, "rw_size larger than GbE part: %lu", - (size_t)gbe_rw_size); - - _flag = (cmd->flags & O_ACCMODE); - - if (_flag != O_RDONLY && - _flag != O_RDWR) - err(EINVAL, "invalid cmd.flags setting"); -} - -void -set_cmd(int argc, char *argv[]) -{ - struct xstate *x = xstatus(); - const char *cmd; - - int rval; - - size_t c; - - for (c = 0; c < items(x->cmd); c++) { - - cmd = x->cmd[c].str; - - if (scmp(argv[2], cmd, MAX_CMD_LEN, &rval) < 0) - err_no_cleanup(EINVAL, - "could not compare command strings"); - if (rval != 0) - continue; /* not the right command */ - - /* valid command found */ - if (argc >= x->cmd[c].argc) { - x->no_cmd = 0; - x->i = c; /* set command */ - - return; - } - - err_no_cleanup(EINVAL, - "Too few args on command '%s'", cmd); - } - - x->no_cmd = 1; -} - -void -set_cmd_args(int argc, char *argv[]) -{ - struct xstate *x = xstatus(); - size_t i = x->i; - struct commands *cmd = &x->cmd[i]; - struct xfile *f = &x->f; - - if (!valid_command(i) || argc < 3) - usage(); - - if (x->no_cmd) - usage(); - - /* Maintainer bug - */ - if (cmd->arg_part && argc < 4) - err(EINVAL, - "arg_part set for command that needs argc4"); - - if (cmd->arg_part && i == CMD_SETMAC) - err(EINVAL, - "arg_part set on CMD_SETMAC"); - - if (i == CMD_SETMAC) { - - if (argc >= 4) - x->mac.str = argv[3]; - else - x->mac.str = x->mac.rmac; - - } else if (cmd->arg_part) { - - f->part = conv_argv_part_num(argv[3]); - } -} - -size_t -conv_argv_part_num(const char *part_str) -{ - unsigned char ch; - - if (part_str[0] == '\0' || part_str[1] != '\0') - err(EINVAL, "Partnum string '%s' wrong length", part_str); - - /* char signedness is implementation-defined - */ - ch = (unsigned char)part_str[0]; - if (ch < '0' || ch > '1') - err(EINVAL, "Bad part number (%c)", ch); - - return (size_t)(ch - '0'); -} - -void -check_command_num(size_t c) -{ - if (!valid_command(c)) - err(EINVAL, "Invalid run_cmd arg: %lu", - (size_t)c); -} - -unsigned char -valid_command(size_t c) -{ - struct xstate *x = xstatus(); - struct commands *cmd; - - if (c >= items(x->cmd)) - return 0; - - cmd = &x->cmd[c]; - - if (c != cmd->chk) - err(EINVAL, - "Invalid cmd chk value (%lu) vs arg: %lu", - cmd->chk, c); - - return 1; -} - -void -cmd_helper_setmac(void) -{ - struct xstate *x = xstatus(); - struct macaddr *mac = &x->mac; - - size_t partnum; - - check_cmd(cmd_helper_setmac, "setmac"); - - printf("MAC address to be written: %s\n", mac->str); - parse_mac_string(); - - for (partnum = 0; partnum < 2; partnum++) - write_mac_part(partnum); -} - -void -parse_mac_string(void) -{ - struct xstate *x = xstatus(); - struct macaddr *mac = &x->mac; - - size_t mac_byte; - - size_t rval; - - if (slen(x->mac.str, 18, &rval) < 0) - err(EINVAL, "Could not determine MAC length"); - - if (rval != 17) - err(EINVAL, "MAC address is the wrong length"); - - memset(mac->mac_buf, 0, sizeof(mac->mac_buf)); - - for (mac_byte = 0; mac_byte < 6; mac_byte++) - set_mac_byte(mac_byte); - - if ((mac->mac_buf[0] | mac->mac_buf[1] | mac->mac_buf[2]) == 0) - err(EINVAL, "Must not specify all-zeroes MAC address"); - - if (mac->mac_buf[0] & 1) - err(EINVAL, "Must not specify multicast MAC address"); -} - -void -set_mac_byte(size_t mac_byte_pos) -{ - struct xstate *x = xstatus(); - struct macaddr *mac = &x->mac; - - char separator; - - size_t mac_str_pos; - size_t mac_nib_pos; - - mac_str_pos = mac_byte_pos * 3; - - if (mac_str_pos < 15) { - if ((separator = mac->str[mac_str_pos + 2]) != ':') - err(EINVAL, "Invalid MAC address separator '%c'", - separator); - } - - for (mac_nib_pos = 0; mac_nib_pos < 2; mac_nib_pos++) - set_mac_nib(mac_str_pos, mac_byte_pos, mac_nib_pos); -} - -void -set_mac_nib(size_t mac_str_pos, - size_t mac_byte_pos, size_t mac_nib_pos) -{ - struct xstate *x = xstatus(); - struct macaddr *mac = &x->mac; - - char mac_ch; - unsigned short hex_num; - - mac_ch = mac->str[mac_str_pos + mac_nib_pos]; - - if ((hex_num = hextonum(mac_ch)) > 15) { - if (hex_num >= 17) - err(EIO, "Randomisation failure"); - else - err(EINVAL, "Invalid character '%c'", - mac->str[mac_str_pos + mac_nib_pos]); - } - - /* 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 - * logically, so we reverse the order. - */ - mac->mac_buf[mac_byte_pos >> 1] |= hex_num << - (((mac_byte_pos & 1) << 3) /* left or right byte? */ - | ((mac_nib_pos ^ 1) << 2)); /* left or right nib? */ -} - -void -write_mac_part(size_t partnum) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - struct macaddr *mac = &x->mac; - - size_t w; - - check_bin(partnum, "part number"); - if (!f->part_valid[partnum]) - return; - - for (w = 0; w < 3; w++) - set_nvm_word(w, partnum, mac->mac_buf[w]); - - printf("Wrote MAC address to part %lu: ", - (size_t)partnum); - print_mac_from_nvm(partnum); -} - -void -cmd_helper_dump(void) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - size_t p; - - check_cmd(cmd_helper_dump, "dump"); - - f->part_valid[0] = good_checksum(0); - f->part_valid[1] = good_checksum(1); - - for (p = 0; p < 2; p++) { - - if (!f->part_valid[p]) { - - fprintf(stderr, - "BAD checksum %04x in part %lu (expected %04x)\n", - nvm_word(NVM_CHECKSUM_WORD, p), - (size_t)p, - calculated_checksum(p)); - } - - printf("MAC (part %lu): ", - (size_t)p); - - print_mac_from_nvm(p); - - hexdump(p); - } -} - -void -print_mac_from_nvm(size_t partnum) -{ - size_t c; - unsigned short val16; - - for (c = 0; c < 3; c++) { - - val16 = nvm_word(c, partnum); - - printf("%02x:%02x", - (unsigned int)(val16 & 0xff), - (unsigned int)(val16 >> 8)); - - if (c == 2) - printf("\n"); - else - printf(":"); - } -} - -void -hexdump(size_t partnum) -{ - size_t c; - size_t row; - unsigned short val16; - - for (row = 0; row < 8; row++) { - - printf("%08lx ", - (size_t)((size_t)row << 4)); - - for (c = 0; c < 8; c++) { - - val16 = nvm_word((row << 3) + c, partnum); - - if (c == 4) - printf(" "); - - printf(" %02x %02x", - (unsigned int)(val16 & 0xff), - (unsigned int)(val16 >> 8)); - - } - - printf("\n"); - } -} - -void -cmd_helper_swap(void) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - check_cmd(cmd_helper_swap, "swap"); - - memcpy( - f->buf + (size_t)GBE_WORK_SIZE, - f->buf, - GBE_PART_SIZE); - - memcpy( - f->buf, - f->buf + (size_t)GBE_PART_SIZE, - GBE_PART_SIZE); - - memcpy( - f->buf + (size_t)GBE_PART_SIZE, - f->buf + (size_t)GBE_WORK_SIZE, - GBE_PART_SIZE); - - set_part_modified(0); - set_part_modified(1); -} - -void -cmd_helper_copy(void) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - check_cmd(cmd_helper_copy, "copy"); - - memcpy( - f->buf + (size_t)((f->part ^ 1) * GBE_PART_SIZE), - f->buf + (size_t)(f->part * GBE_PART_SIZE), - GBE_PART_SIZE); - - set_part_modified(f->part ^ 1); -} - -void -cmd_helper_cat(void) -{ - struct xstate *x = xstatus(); - - check_cmd(cmd_helper_cat, "cat"); - - x->cat = 0; - cat(0); -} - -void -cmd_helper_cat16(void) -{ - struct xstate *x = xstatus(); - - check_cmd(cmd_helper_cat16, "cat16"); - - x->cat = 1; - cat(1); -} - -void -cmd_helper_cat128(void) -{ - struct xstate *x = xstatus(); - - check_cmd(cmd_helper_cat128, "cat128"); - - x->cat = 15; - cat(15); -} - -void -cat(size_t nff) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - size_t p; - size_t ff; - - p = 0; - ff = 0; - - if ((size_t)x->cat != nff) { - - err(ECANCELED, "erroneous call to cat"); - } - - fflush(NULL); - - memset(f->pad, 0xff, GBE_PART_SIZE); - - for (p = 0; p < 2; p++) { - - cat_buf(f->bufcmp + - (size_t)(p * (f->gbe_file_size >> 1))); - - for (ff = 0; ff < nff; ff++) { - - cat_buf(f->pad); - } - } -} - -void -cat_buf(unsigned char *b) -{ - if (b == NULL) - err(errno, "null pointer in cat command"); - - if (rw_file_exact(STDOUT_FILENO, b, - GBE_PART_SIZE, 0, IO_WRITE, LOOP_EAGAIN, LOOP_EINTR, - MAX_ZERO_RW_RETRY, OFF_ERR) < 0) - err(errno, "stdout: cat"); -} -void -check_cmd(void (*fn)(void), - const char *name) -{ - struct xstate *x = xstatus(); - size_t i = x->i; - - if (x->cmd[i].run != fn) - err(ECANCELED, "Running %s, but cmd %s is set", - name, x->cmd[i].str); - - /* prevent second command - */ - for (i = 0; i < items(x->cmd); i++) - x->cmd[i].run = cmd_helper_err; -} - -void -cmd_helper_err(void) -{ - err(ECANCELED, - "Erroneously running command twice"); -} diff --git a/util/nvmutil/lib/file.c b/util/nvmutil/lib/file.c deleted file mode 100644 index ea2bcd0b..00000000 --- a/util/nvmutil/lib/file.c +++ /dev/null @@ -1,1136 +0,0 @@ -/* SPDX-License-Identifier: MIT - * Copyright (c) 2026 Leah Rowe <leah@libreboot.org> - * - * Pathless i/o, and some stuff you probably never saw. - * Be nice to the demon. - */ - - -#include <sys/types.h> -#include <sys/stat.h> - -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -/* for openat2: */ -#ifdef __linux__ -#include <linux/openat2.h> -#include <sys/syscall.h> -#endif - -#include "../include/common.h" - -/* check that a file changed - */ - -int -same_file(int fd, struct stat *st_old, - int check_size) -{ - struct stat st; - int saved_errno = errno; - - /* TODO: null/-1 checks - * like this can be - * generalised - */ - if (st_old == NULL) { - errno = EFAULT; - goto err_same_file; - } - if (fd < 0) { - errno = EBADF; - goto err_same_file; - } - - if (fstat(fd, &st) == -1) - goto err_same_file; - - if (fd_verify_regular(fd, st_old, &st) < 0) - goto err_same_file; - - if (check_size && - st.st_size != st_old->st_size) - goto err_same_file; - - errno = saved_errno; - return 0; - -err_same_file: - - if (errno == saved_errno) - errno = ESTALE; - - return -1; -} - -/* open() but with abort traps - */ -/* TODO: also support other things here than files. - and then use, throughout the program. - in particular, use of openat might help - (split the path) - (see: link attack mitigations throughout nvmutil) - - make it return, and handle the return value/errno - - (this could return e.g. EINTR) - - TODO: this function is not used by mkhtemp, nor will - it probably be, it's currently used by nvmutil, - for opening intel gbe nvm config files. i can - probably remove it though and unify witth some - of the verification code now used for mkhtemp - -TODO: and don't abort. return -1. and handle in the caller. - -minor obstacle: the mkhtemp code always requires absolute -paths, whereas the gbe editor takes relative paths. - */ -void -xopen(int *fd_ptr, const char *path, int flags, struct stat *st) -{ - if ((*fd_ptr = open(path, flags)) < 0) - err(errno, "%s", path); - - if (fstat(*fd_ptr, st) < 0) - err(errno, "%s: stat", path); - - if (!S_ISREG(st->st_mode)) - err(errno, "%s: not a regular file", path); - - if (lseek_on_eintr(*fd_ptr, 0, SEEK_CUR, 1, 1) == (off_t)-1) - err(errno, "%s: file not seekable", path); -} - -/* fsync() the directory of a file, - * useful for atomic writes - */ - -int -fsync_dir(const char *path) -{ - int saved_errno = errno; - - size_t pathlen = 0; - size_t maxlen = 0; - - char *dirbuf = NULL; - int dirfd = -1; - - char *slash = NULL; - struct stat st = {0}; - - int close_errno; - -#if defined(PATH_LEN) && \ - (PATH_LEN) >= 256 - maxlen = PATH_LEN; -#else - maxlen = 4096; -#endif - - if (path == NULL) { - errno = EFAULT; - goto err_fsync_dir; - } - - if (slen(path, maxlen, &pathlen) < 0) - goto err_fsync_dir; - - if (pathlen >= maxlen || pathlen < 0) { - errno = EMSGSIZE; - goto err_fsync_dir; - } - - if (pathlen == 0) - { - errno = EINVAL; - goto err_fsync_dir; - } - - dirbuf = malloc(pathlen + 1); - if (dirbuf == NULL) { - - errno = ENOMEM; - goto err_fsync_dir; - } - - memcpy(dirbuf, path, pathlen + 1); - slash = strrchr(dirbuf, '/'); - - if (slash != NULL) { - *slash = '\0'; - if (*dirbuf == '\0') { - dirbuf[0] = '/'; - dirbuf[1] = '\0'; - } - } else { - dirbuf[0] = '.'; - dirbuf[1] = '\0'; - } - - dirfd = fs_open(dirbuf, - O_RDONLY | O_CLOEXEC | O_NOCTTY -#ifdef O_DIRECTORY - | O_DIRECTORY -#endif -#ifdef O_NOFOLLOW - | O_NOFOLLOW -#endif -); - if (dirfd < 0) - goto err_fsync_dir; - - if (fstat(dirfd, &st) < 0) - goto err_fsync_dir; - - if (!S_ISDIR(st.st_mode)) { - - errno = ENOTDIR; - goto err_fsync_dir; - } - - /* sync file on disk */ - if (fsync_on_eintr(dirfd) == -1) - goto err_fsync_dir; - - if (close_on_eintr(dirfd) == -1) { - - dirfd = -1; - goto err_fsync_dir; - } - - if (dirbuf != NULL) { - - free(dirbuf); - dirbuf = NULL; - } - - dirbuf = NULL; - - errno = saved_errno; - return 0; - -err_fsync_dir: - - if (errno == saved_errno) - errno = EIO; - - if (dirbuf != NULL) { - - free(dirbuf); - dirbuf = NULL; - } - - if (dirfd >= 0) { - - close_errno = errno; - (void) close_on_eintr(dirfd); - errno = close_errno; - dirfd = -1; - } - - return -1; -} - -/* - * Safe I/O functions wrapping around - * read(), write() and providing a portable - * analog of both pread() and pwrite(). - * These functions are designed for maximum - * robustness, checking NULL inputs, overflowed - * outputs, and all kinds of errors that the - * standard libc functions don't. - * - * Looping on EINTR and EAGAIN is supported. - * EINTR/EAGAIN looping is done indefinitely. - */ - -/* rw_file_exact() - Read perfectly or die - * - * Read/write, and absolutely insist on an - * absolute read; e.g. if 100 bytes are - * requested, this MUST return 100. - * - * This function will never return zero. - * It will only return below (error), - * or above (success). On error, -1 is - * returned and errno is set accordingly. - * - * Zero-byte returns are not allowed. - * It will re-spin a finite number of - * times upon zero-return, to recover, - * otherwise it will return an error. - */ - -ssize_t -rw_file_exact(int fd, unsigned char *mem, size_t nrw, - off_t off, int rw_type, int loop_eagain, - int loop_eintr, size_t max_retries, - int off_reset) -{ - ssize_t rval; - ssize_t rc; - - size_t nrw_cur; - - off_t off_cur; - void *mem_cur; - - size_t retries_on_zero; - - int saved_errno = errno; - - rval = 0; - - rc = 0; - retries_on_zero = 0; - - if (io_args(fd, mem, nrw, off, rw_type) == -1) - goto err_rw_file_exact; - - while (1) { - - /* Prevent theoretical overflow */ - if (rval >= 0 && (size_t)rval > (nrw - rc)) { - errno = EOVERFLOW; - goto err_rw_file_exact; - } - - rc += rval; - if ((size_t)rc >= nrw) - break; - - mem_cur = (void *)(mem + (size_t)rc); - nrw_cur = (size_t)(nrw - (size_t)rc); - - if (off < 0) { - errno = EOVERFLOW; - goto err_rw_file_exact; - } - - off_cur = off + (off_t)rc; - - rval = prw(fd, mem_cur, nrw_cur, off_cur, - rw_type, loop_eagain, loop_eintr, - off_reset); - - if (rval < 0) - goto err_rw_file_exact; - - if (rval == 0) { - if (retries_on_zero++ < max_retries) - continue; - - errno = EIO; - goto err_rw_file_exact; - } - - retries_on_zero = 0; - } - - if ((size_t)rc != nrw) { - - errno = EIO; - goto err_rw_file_exact; - } - - rval = rw_over_nrw(rc, nrw); - if (rval < 0) - goto err_rw_file_exact; - - errno = saved_errno; - - return rval; - -err_rw_file_exact: - - if (errno == saved_errno) - errno = EIO; - - return -1; -} - -/* prw() - portable read-write with more - * safety checks than barebones libc - * - * 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: REAL_POS_IO=1 - * - * A fallback is provided for regular read/write. - * 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 - * - * race conditions on non-libc pread/pwrite: - * if a file offset changes, abort, to mitage. - * - * 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 - */ - -ssize_t -prw(int fd, void *mem, size_t nrw, - off_t off, int rw_type, - int loop_eagain, int loop_eintr, - int off_reset) -{ - ssize_t rval; - ssize_t r; - int positional_rw; - struct stat st; -#if !defined(REAL_POS_IO) || \ - REAL_POS_IO < 1 - off_t verified; - off_t off_orig; - off_t off_last; -#endif - int saved_errno = errno; - - if (io_args(fd, mem, nrw, off, rw_type) - == -1) - goto err_prw; - - r = -1; - - /* do not use loop_eagain on - * normal files - */ - - if (!loop_eagain) { - /* check whether the file - * changed - */ - - if (check_file(fd, &st) == -1) - goto err_prw; - } - - if (rw_type >= IO_PREAD) - positional_rw = 1; /* pread/pwrite */ - else - positional_rw = 0; /* read/write */ - -try_rw_again: - - if (!positional_rw) { -#if defined(REAL_POS_IO) && \ - REAL_POS_IO > 0 -real_pread_pwrite: -#endif - if (rw_type == IO_WRITE) - r = write(fd, mem, nrw); - else if (rw_type == IO_READ) - r = read(fd, mem, nrw); -#if defined(REAL_POS_IO) && \ - REAL_POS_IO > 0 - else if (rw_type == IO_PWRITE) - r = pwrite(fd, mem, nrw, off); - else if (rw_type == IO_PREAD) - r = pread(fd, mem, nrw, off); -#endif - - if (r == -1 && (errno == try_err(loop_eintr, EINTR) - || errno == try_err(loop_eagain, EAGAIN))) - goto try_rw_again; - - rval = rw_over_nrw(r, nrw); - if (rval < 0) - goto err_prw; - - errno = saved_errno; - - return rval; - } - -#if defined(REAL_POS_IO) && \ - REAL_POS_IO > 0 - goto real_pread_pwrite; -#else - if ((off_orig = lseek_on_eintr(fd, (off_t)0, SEEK_CUR, - loop_eagain, loop_eintr)) == (off_t)-1) { - r = -1; - } else if (lseek_on_eintr(fd, off, SEEK_SET, - loop_eagain, loop_eintr) == (off_t)-1) { - r = -1; - } else { - verified = lseek_on_eintr(fd, (off_t)0, SEEK_CUR, - loop_eagain, loop_eintr); - - /* 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 { - /* 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); - - if (off != verified) { - - errno = EBUSY; - goto err_prw; - } - - if (rw_type == IO_PREAD) - r = read(fd, mem, nrw); - else if (rw_type == IO_PWRITE) - r = write(fd, mem, nrw); - - if (rw_over_nrw(r, nrw) == -1) - break; - - } while (r == -1 && - (errno == try_err(loop_eintr, EINTR) || - errno == try_err(loop_eagain, EAGAIN))); - } - - saved_errno = errno; - - off_last = lseek_on_eintr(fd, off_orig, SEEK_SET, - loop_eagain, loop_eintr); - - if (off_last != off_orig) { - errno = saved_errno; - goto err_prw; - } - - errno = saved_errno; - - rval = rw_over_nrw(r, nrw); - if (rval < 0) - goto err_prw; - - errno = saved_errno; - - return rval; - -#endif - -err_prw: - - if (errno == saved_errno) - errno = EIO; - - return -1; -} - -int -io_args(int fd, void *mem, size_t nrw, - off_t off, int rw_type) -{ - int saved_errno = errno; - - /* obviously */ - if (mem == NULL) { - - errno = EFAULT; - goto err_io_args; - } - - /* uninitialised fd */ - if (fd < 0) { - - errno = EBADF; - goto err_io_args; - } - - /* negative offset */ - if (off < 0) { - - errno = ERANGE; - goto err_io_args; - } - - /* prevent zero-byte rw */ - if (!nrw) - goto err_io_args; - - /* prevent overflow */ - if (nrw > (size_t)SSIZE_MAX) { - - errno = ERANGE; - goto err_io_args; - } - - /* prevent overflow */ - if (((size_t)off + nrw) < (size_t)off) { - - errno = ERANGE; - goto err_io_args; - } - - if (rw_type > IO_PWRITE) { - - errno = EINVAL; - goto err_io_args; - } - - errno = saved_errno; - - return 0; - -err_io_args: - - if (errno == saved_errno) - errno = EINVAL; - - return -1; -} - -int -check_file(int fd, struct stat *st) -{ - int saved_errno = errno; - - if (fd < 0) { - errno = EBADF; - goto err_is_file; - } - - if (st == NULL) { - errno = EFAULT; - goto err_is_file; - } - - if (fstat(fd, st) == -1) - goto err_is_file; - - if (!S_ISREG(st->st_mode)) { - - errno = EBADF; - goto err_is_file; - } - - errno = saved_errno; - - return 0; - -err_is_file: - - if (errno == saved_errno) - errno = EINVAL; - - return -1; -} - -/* POSIX can say whatever it wants. - * specification != implementation - */ - -ssize_t -rw_over_nrw(ssize_t r, size_t nrw) -{ - int saved_errno = errno; - - /* not a libc bug, but we - * don't like the number zero - */ - if (!nrw) - goto err_rw_over_nrw; - - if (r == -1) - return r; - - if ((size_t) - r > SSIZE_MAX) { - - /* Theoretical buggy libc - * check. Extremely academic. - * - * Specifications never - * allow this return value - * to exceed SSIZE_T, but - * spec != implementation - * - * Check this after using - * [p]read() or [p]write() - * - * NOTE: here, we assume - * ssize_t integers are the - * same size as SSIZE_T - */ - - errno = ERANGE; - goto err_rw_over_nrw; - } - - /* Theoretical buggy libc: - * Should never return a number of - * bytes above the requested length. - */ - if ((size_t)r > nrw) { - - errno = ERANGE; - goto err_rw_over_nrw; - } - - errno = saved_errno; - - return r; - -err_rw_over_nrw: - - if (errno == saved_errno) - errno = EIO; - - return -1; -} - -#if !defined(REAL_POS_IO) || \ - REAL_POS_IO < 1 -off_t -lseek_on_eintr(int fd, off_t off, int whence, - int loop_eagain, int loop_eintr) -{ - off_t old; - - old = -1; - - do { - old = lseek(fd, off, whence); - } while (old == (off_t)-1 && ( - errno == try_err(loop_eintr, EINTR) || - errno == try_err(loop_eintr, ETXTBSY) || - errno == try_err(loop_eagain, EAGAIN) || - errno == try_err(loop_eagain, EWOULDBLOCK))); - - return old; -} -#endif - -int -try_err(int loop_err, int errval) -{ - if (loop_err) - return errval; - - return -1; -} - -int -close_on_eintr(int fd) -{ - int r; - int saved_errno = errno; - - do { - r = close(fd); - } while (r == -1 && ( - errno == EINTR || errno == EAGAIN || - errno == EWOULDBLOCK || errno == ETXTBSY)); - - if (r >= 0) - errno = saved_errno; - - return r; -} - -int -fsync_on_eintr(int fd) -{ - int r; - int saved_errno = errno; - - do { - r = fsync(fd); - } while (r == -1 && (errno == EINTR || errno == EAGAIN || - errno == ETXTBSY || errno == EWOULDBLOCK)); - - if (r >= 0) - errno = saved_errno; - - return r; -} - -int -fs_rename_at(int olddirfd, const char *old, - int newdirfd, const char *new) -{ - if (new == NULL || old == NULL) { - - errno = EFAULT; - return -1; - } - - if (olddirfd < 0 || newdirfd < 0) { - - errno = EBADF; - return -1; - } - - return renameat(olddirfd, old, newdirfd, new); -} - -/* secure open, based on - * relative path to root - * - * always a fixed fd for / - * see: rootfs() - */ -int -fs_open(const char *path, int flags) -{ - struct filesystem *fs; - const char *rel; - - if (path == NULL) { - errno = EFAULT; - return -1; - } - - if (path[0] != '/') { - errno = EINVAL; - return -1; - } - - fs = rootfs(); - if (fs == NULL) - return -1; - - rel = path + 1; - - return fs_resolve_at(fs->rootfd, rel, flags); -} - -/* singleton function - * that returns a fixed - * descriptor of / - * - * used throughout, for - * repeated integrity checks - */ -struct filesystem * -rootfs(void) -{ - static struct filesystem global_fs; - static int fs_initialised = 0; - - if (!fs_initialised) { - - global_fs.rootfd = - open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC); - - if (global_fs.rootfd < 0) - return NULL; - - fs_initialised = 1; - } - - return &global_fs; -} - -/* filesystem sandboxing. - * (in userspace) - */ -int -fs_resolve_at(int dirfd, const char *path, int flags) -{ - int nextfd = -1; - int curfd; - const char *p; - char name[256]; - int saved_errno = errno; - int r; - int is_last; - - if (dirfd < 0 || path == NULL || *path == '\0') { - errno = EINVAL; - return -1; - } - - p = path; - curfd = dirfd; /* start here */ - - for (;;) { - r = fs_next_component(&p, name, sizeof(name)); - if (r < 0) - goto err; - if (r == 0) - break; - - is_last = (*p == '\0'); - - nextfd = fs_open_component(curfd, name, flags, is_last); - if (nextfd < 0) - goto err; - - /* close previous fd IF it is not the original input */ - if (curfd != dirfd) { - (void) close_on_eintr(curfd); - } - - curfd = nextfd; - nextfd = -1; - } - - errno = saved_errno; - return curfd; - -err: - saved_errno = errno; - - if (nextfd >= 0) - (void) close_on_eintr(nextfd); - - /* close curfd only if it's not the original */ - if (curfd != dirfd && curfd >= 0) - (void) close_on_eintr(curfd); - - errno = saved_errno; - return -1; -} - -int -fs_next_component(const char **p, - char *name, size_t namesz) -{ - const char *s = *p; - size_t len = 0; -#if defined(PATH_LEN) && \ -(PATH_LEN) >= 256 - size_t maxlen = PATH_LEN; -#else - size_t maxlen = 4096; -#endif - - while (*s == '/') - s++; - - if (*s == '\0') { - *p = s; - return 0; - } - - while (s[len] != '/' && s[len] != '\0') - len++; - - if (len == 0 || len >= namesz || - len >= maxlen) { - errno = ENAMETOOLONG; - return -1; - } - - memcpy(name, s, len); - name[len] = '\0'; - - /* reject . and .. */ - if ((name[0] == '.' && name[1] == '\0') || - (name[0] == '.' && name[1] == '.' && name[2] == '\0')) { - errno = EPERM; - return -1; - } - - *p = s + len; - return 1; -} - -int -fs_open_component(int dirfd, const char *name, - int flags, int is_last) -{ - int fd; - struct stat st; - - fd = openat2p(dirfd, name, - (is_last ? flags : (O_RDONLY | O_DIRECTORY)) | - O_NOFOLLOW | O_CLOEXEC, (flags & O_CREAT) ? 0600 : 0); - - /* the patient always lies - */ - if (!is_last) { - - if (fd < 0) { - errno = EBADF; - return -1; - } - - if (fstat(fd, &st) < 0) - return -1; - - if (!S_ISDIR(st.st_mode)) { - - (void) close_on_eintr(fd); - errno = ENOTDIR; - return -1; - } - } - - return fd; -} - -int -fs_dirname_basename(const char *path, - char **dir, char **base, - int allow_relative) -{ - char *buf; - char *slash; - size_t len; - int rval; -#if defined(PATH_LEN) && \ -(PATH_LEN) >= 256 - size_t maxlen = PATH_LEN; -#else - size_t maxlen = 4096; -#endif - - if (path == NULL || dir == NULL || base == NULL) - return -1; - - if (slen(path, maxlen, &len) < 0) - return -1; - - buf = malloc(len + 1); - if (buf == NULL) - return -1; - - memcpy(buf, path, len + 1); - - /* strip trailing slashes */ - while (len > 1 && buf[len - 1] == '/') - buf[--len] = '\0'; - - slash = strrchr(buf, '/'); - - if (slash) { - - *slash = '\0'; - *dir = buf; - *base = slash + 1; - - if (**dir == '\0') { - (*dir)[0] = '/'; - (*dir)[1] = '\0'; - } - } else if (allow_relative) { - - *dir = strdup("."); - *base = buf; - } else { - errno = EINVAL; - free(buf); - return -1; - } - - return 0; -} - -/* portable wrapper for use of openat2 on linux, - * with fallback for others e.g. openbsd - * - * BONUS: arg checks - * TODO: consider EINTR/EAGAIN retry loop - */ -int -openat2p(int dirfd, const char *path, - int flags, mode_t mode) -{ -#ifdef __linux__ - struct open_how how = { - .flags = flags, - .mode = mode, - .resolve = - RESOLVE_BENEATH | - RESOLVE_NO_SYMLINKS | - RESOLVE_NO_MAGICLINKS - }; - int saved_errno = errno; - int rval; -#endif - - if (dirfd < 0) { - errno = EBADF; - return -1; - } - - if (path == NULL) { - errno = EFAULT; - return -1; - } - -retry: - errno = 0; - -#ifdef __linux__ - /* more secure than regular openat, - * but linux-only at the time of writing - */ - rval = syscall(SYS_openat2, dirfd, path, &how, sizeof(how)); -#else - /* less secure, but e.g. openbsd - * doesn't have openat2 yet - */ - rval = openat(dirfd, path, flags, mode); -#endif - if (rval == -1 && ( - errno == EINTR || - errno == EAGAIN || - errno == EWOULDBLOCK || - errno == ETXTBSY)) - goto retry; - - if (rval >= 0) - errno = saved_errno; - - return rval; -} - -int -mkdirat_on_eintr( /* <-- say that 10 times to please the demon */ - int dirfd, - const char *path, mode_t mode) -{ - int saved_errno = errno; - int rval; - - if (dirfd < 0) { - errno = EBADF; - return -1; - } - - if (path == NULL) { - errno = EFAULT; - return -1; - } - -retry: - errno = 0; - rval = mkdirat(dirfd, path, mode); - - if (rval == -1 && ( - errno == EINTR || - errno == EAGAIN || - errno == EWOULDBLOCK || - errno == ETXTBSY)) - goto retry; - - if (rval >= 0) - errno = saved_errno; - - return rval; -} diff --git a/util/nvmutil/lib/io.c b/util/nvmutil/lib/io.c deleted file mode 100644 index 94bde87e..00000000 --- a/util/nvmutil/lib/io.c +++ /dev/null @@ -1,673 +0,0 @@ -/* SPDX-License-Identifier: MIT - * Copyright (c) 2026 Leah Rowe <leah@libreboot.org> - * - * I/O functions specific to nvmutil. - */ - -#include <sys/types.h> -#include <sys/stat.h> - -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "../include/common.h" - -void -open_gbe_file(void) -{ - struct xstate *x = xstatus(); - struct commands *cmd = &x->cmd[x->i]; - struct xfile *f = &x->f; - - int _flags; - - xopen(&f->gbe_fd, f->fname, - cmd->flags | O_BINARY | - O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, &f->gbe_st); - - if (f->gbe_st.st_nlink > 1) - err(EINVAL, - "%s: warning: file has multiple (%lu) hard links\n", - f->fname, (size_t)f->gbe_st.st_nlink); - - if (f->gbe_st.st_nlink == 0) - err(EIO, "%s: file unlinked while open", f->fname); - - _flags = fcntl(f->gbe_fd, F_GETFL); - if (_flags == -1) - err(errno, "%s: fcntl(F_GETFL)", f->fname); - - /* 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); - - f->gbe_file_size = f->gbe_st.st_size; - - switch (f->gbe_file_size) { - case SIZE_8KB: - case SIZE_16KB: - case SIZE_128KB: - break; - default: - err(EINVAL, "File size must be 8KB, 16KB or 128KB"); - } - - if (lock_file(f->gbe_fd, cmd->flags) == -1) - err(errno, "%s: can't lock", f->fname); -} - -void -copy_gbe(void) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - read_file(); - - if (f->gbe_file_size == SIZE_8KB) - return; - - memcpy(f->buf + (size_t)GBE_PART_SIZE, - f->buf + (size_t)(f->gbe_file_size >> 1), - (size_t)GBE_PART_SIZE); -} - -void -read_file(void) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - struct stat _st; - ssize_t _r; - - /* read main file - */ - _r = rw_file_exact(f->gbe_fd, f->buf, f->gbe_file_size, - 0, IO_PREAD, NO_LOOP_EAGAIN, LOOP_EINTR, - MAX_ZERO_RW_RETRY, OFF_ERR); - - if (_r < 0) - err(errno, "%s: read failed", f->fname); - - /* copy to tmpfile - */ - _r = rw_file_exact(f->tmp_fd, f->buf, f->gbe_file_size, - 0, IO_PWRITE, NO_LOOP_EAGAIN, LOOP_EINTR, - MAX_ZERO_RW_RETRY, OFF_ERR); - - if (_r < 0) - err(errno, "%s: %s: copy failed", - f->fname, f->tname); - - /* file size comparison - */ - if (fstat(f->tmp_fd, &_st) == -1) - err(errno, "%s: stat", f->tname); - - f->gbe_tmp_size = _st.st_size; - - if (f->gbe_tmp_size != f->gbe_file_size) - err(EIO, "%s: %s: not the same size", - f->fname, f->tname); - - /* needs sync, for verification - */ - if (fsync_on_eintr(f->tmp_fd) == -1) - err(errno, "%s: fsync (tmpfile copy)", f->tname); - - _r = rw_file_exact(f->tmp_fd, f->bufcmp, f->gbe_file_size, - 0, IO_PREAD, NO_LOOP_EAGAIN, LOOP_EINTR, - MAX_ZERO_RW_RETRY, OFF_ERR); - - if (_r < 0) - err(errno, "%s: read failed (cmp)", f->tname); - - if (memcmp(f->buf, f->bufcmp, f->gbe_file_size) != 0) - err(errno, "%s: %s: read contents differ (pre-test)", - f->fname, f->tname); -} - -void -write_gbe_file(void) -{ - struct xstate *x = xstatus(); - struct commands *cmd = &x->cmd[x->i]; - struct xfile *f = &x->f; - - size_t p; - unsigned char update_checksum; - - if ((cmd->flags & O_ACCMODE) == O_RDONLY) - return; - - if (same_file(f->tmp_fd, &f->tmp_st, 0) < 0) - err(errno, "%s: file inode/device changed", f->tname); - - if (same_file(f->gbe_fd, &f->gbe_st, 1) < 0) - err(errno, "%s: file has changed", f->fname); - - update_checksum = cmd->chksum_write; - - for (p = 0; p < 2; p++) { - if (!f->part_modified[p]) - continue; - - if (update_checksum) - set_checksum(p); - - rw_gbe_file_part(p, IO_PWRITE, "pwrite"); - } -} - -void -rw_gbe_file_part(size_t p, int rw_type, - const char *rw_type_str) -{ - struct xstate *x = xstatus(); - struct commands *cmd = &x->cmd[x->i]; - struct xfile *f = &x->f; - - ssize_t rval; - - off_t file_offset; - - size_t gbe_rw_size; - unsigned char *mem_offset; - - gbe_rw_size = cmd->rw_size; - - if (rw_type < IO_PREAD || rw_type > IO_PWRITE) - err(errno, "%s: %s: part %lu: invalid rw_type, %d", - f->fname, rw_type_str, (size_t)p, rw_type); - - mem_offset = gbe_mem_offset(p, rw_type_str); - file_offset = (off_t)gbe_file_offset(p, rw_type_str); - - rval = rw_gbe_file_exact(f->tmp_fd, mem_offset, - gbe_rw_size, file_offset, rw_type); - - if (rval == -1) - err(errno, "%s: %s: part %lu", - f->fname, rw_type_str, (size_t)p); - - if ((size_t)rval != gbe_rw_size) - err(EIO, "%s: partial %s: part %lu", - f->fname, rw_type_str, (size_t)p); -} - -void -write_to_gbe_bin(void) -{ - struct xstate *x = xstatus(); - struct commands *cmd = &x->cmd[x->i]; - struct xfile *f = &x->f; - - int saved_errno; - int mv; - - if ((cmd->flags & O_ACCMODE) != O_RDWR) - return; - - write_gbe_file(); - - /* 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); - - check_written_part(0); - check_written_part(1); - - report_io_err_rw(); - - if (f->io_err_gbe) - err(EIO, "%s: bad write", f->fname); - - saved_errno = errno; - - if (close_on_eintr(f->tmp_fd) == -1) { - f->tmp_fd = -1; - - fprintf(stderr, "FAIL: %s: close\n", f->tname); - f->io_err_gbe_bin = 1; - } - f->tmp_fd = -1; - - if (close_on_eintr(f->gbe_fd) == -1) { - f->gbe_fd = -1; - - fprintf(stderr, "FAIL: %s: close\n", f->fname); - f->io_err_gbe_bin = 1; - } - f->gbe_fd = -1; - - 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; - - if (!f->io_err_gbe_bin) { - - mv = gbe_mv(); - - if (mv < 0) { - - f->io_err_gbe_bin = 1; - - fprintf(stderr, "%s: %s\n", - f->fname, strerror(errno)); - } else { - - /* removed by rename - */ - - if (f->tname != NULL) { - free(f->tname); - f->tname = NULL; - } - - f->tname = NULL; - } - } - - if (!f->io_err_gbe_bin) - return; - - fprintf(stderr, "FAIL (rename): %s: skipping fsync\n", - f->fname); - if (errno) - fprintf(stderr, - "errno %d: %s\n", errno, strerror(errno)); -} - -void -check_written_part(size_t p) -{ - struct xstate *x = xstatus(); - struct commands *cmd = &x->cmd[x->i]; - struct xfile *f = &x->f; - - ssize_t rval; - - size_t gbe_rw_size; - - off_t file_offset; - unsigned char *mem_offset; - - unsigned char *buf_restore; - - if (!f->part_modified[p]) - return; - - gbe_rw_size = cmd->rw_size; - - mem_offset = gbe_mem_offset(p, "pwrite"); - file_offset = (off_t)gbe_file_offset(p, "pwrite"); - - memset(f->pad, 0xff, sizeof(f->pad)); - - if (same_file(f->tmp_fd, &f->tmp_st, 0) < 0) - err(errno, "%s: file inode/device changed", f->tname); - - if (same_file(f->gbe_fd, &f->gbe_st, 1) < 0) - err(errno, "%s: file changed during write", f->fname); - - rval = rw_gbe_file_exact(f->tmp_fd, f->pad, - gbe_rw_size, file_offset, IO_PREAD); - - if (rval == -1) - f->rw_check_err_read[p] = f->io_err_gbe = 1; - else if ((size_t)rval != gbe_rw_size) - f->rw_check_partial_read[p] = f->io_err_gbe = 1; - else if (memcmp(mem_offset, f->pad, gbe_rw_size) != 0) - f->rw_check_bad_part[p] = f->io_err_gbe = 1; - - if (f->rw_check_err_read[p] || - f->rw_check_partial_read[p]) - return; - - /* 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) - */ - - buf_restore = f->buf; - - /* good_checksum works on f->buf - * so let's change f->buf for now - */ - - f->buf = f->pad; - - if (good_checksum(0)) - f->post_rw_checksum[p] = 1; - - f->buf = buf_restore; -} - -void -report_io_err_rw(void) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - size_t p; - - if (!f->io_err_gbe) - return; - - for (p = 0; p < 2; p++) { - if (!f->part_modified[p]) - continue; - - if (f->rw_check_err_read[p]) - fprintf(stderr, - "%s: pread: p%lu (post-verification)\n", - f->fname, (size_t)p); - if (f->rw_check_partial_read[p]) - fprintf(stderr, - "%s: partial pread: p%lu (post-verification)\n", - f->fname, (size_t)p); - if (f->rw_check_bad_part[p]) - fprintf(stderr, - "%s: pwrite: corrupt write on p%lu\n", - f->fname, (size_t)p); - - if (f->rw_check_err_read[p] || - f->rw_check_partial_read[p]) { - fprintf(stderr, - "%s: p%lu: skipped checksum verification " - "(because read failed)\n", - f->fname, (size_t)p); - - continue; - } - - fprintf(stderr, "%s: ", f->fname); - - if (f->post_rw_checksum[p]) - fprintf(stderr, "GOOD"); - else - fprintf(stderr, "BAD"); - - fprintf(stderr, " checksum in p%lu on-disk.\n", - (size_t)p); - - if (f->post_rw_checksum[p]) { - fprintf(stderr, - " This does NOT mean it's safe. it may be\n" - " salvageable if you use the cat feature.\n"); - } - } -} - -int -gbe_mv(void) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - int rval; - - int saved_errno; - int tmp_gbe_bin_exists; - - char *dest_tmp; - int dest_fd; - - /* will be set 0 if it doesn't - */ - tmp_gbe_bin_exists = 1; - - dest_tmp = NULL; - dest_fd = -1; - - saved_errno = errno; - - rval = rename(f->tname, f->fname); - - if (rval > -1) { - - /* rename on same filesystem - */ - - tmp_gbe_bin_exists = 0; - - if (fsync_dir(f->fname) < 0) { - f->io_err_gbe_bin = 1; - rval = -1; - } - - goto ret_gbe_mv; - } - - if (errno != EXDEV) - goto ret_gbe_mv; - - /* - * 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 - */ - if (new_tmpfile(&dest_fd, &f->fname) < 1) - goto ret_gbe_mv; - if (dest_tmp == NULL) - goto ret_gbe_mv; - - /* copy data - */ - rval = rw_file_exact(f->tmp_fd, f->bufcmp, - f->gbe_file_size, 0, IO_PREAD, - NO_LOOP_EAGAIN, LOOP_EINTR, - MAX_ZERO_RW_RETRY, OFF_ERR); - - if (rval < 0) - goto ret_gbe_mv; - - rval = rw_file_exact(dest_fd, f->bufcmp, - f->gbe_file_size, 0, IO_PWRITE, - NO_LOOP_EAGAIN, LOOP_EINTR, - MAX_ZERO_RW_RETRY, OFF_ERR); - - if (rval < 0) - goto ret_gbe_mv; - - if (fsync_on_eintr(dest_fd) == -1) - goto ret_gbe_mv; - - if (close_on_eintr(dest_fd) == -1) { - dest_fd = -1; - goto ret_gbe_mv; - } - dest_fd = -1; - - if (rename(dest_tmp, f->fname) == -1) - goto ret_gbe_mv; - - if (fsync_dir(f->fname) < 0) { - f->io_err_gbe_bin = 1; - goto ret_gbe_mv; - } - - if (dest_tmp != NULL) { - free(dest_tmp); - dest_tmp = NULL; - } - - dest_tmp = NULL; - -ret_gbe_mv: - - /* TODO: this whole section is bloat. - it can be generalised - */ - - if (f->gbe_fd > -1) { - if (close_on_eintr(f->gbe_fd) < 0) { - f->gbe_fd = -1; - rval = -1; - } - f->gbe_fd = -1; - - if (fsync_dir(f->fname) < 0) { - f->io_err_gbe_bin = 1; - rval = -1; - } - } - - if (f->tmp_fd > -1) { - if (close_on_eintr(f->tmp_fd) < 0) { - f->tmp_fd = -1; - rval = -1; - } - f->tmp_fd = -1; - } - - /* before this function is called, - * tmp_fd may have been moved - */ - if (tmp_gbe_bin_exists) { - if (unlink(f->tname) < 0) - rval = -1; - else - tmp_gbe_bin_exists = 0; - } - - if (rval < 0) { - /* if nothing set errno, - * we assume EIO, or we - * use what was set - */ - if (errno == saved_errno) - errno = EIO; - } else { - errno = saved_errno; - } - - return rval; -} - -/* 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. - */ -unsigned char * -gbe_mem_offset(size_t p, const char *f_op) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - off_t gbe_off; - - gbe_off = gbe_x_offset(p, f_op, "mem", - GBE_PART_SIZE, GBE_WORK_SIZE); - - return (unsigned char *) - (f->buf + (size_t)gbe_off); -} - -/* 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. - */ -off_t -gbe_file_offset(size_t p, const char *f_op) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - off_t gbe_file_half_size; - - gbe_file_half_size = f->gbe_file_size >> 1; - - return gbe_x_offset(p, f_op, "file", - gbe_file_half_size, f->gbe_file_size); -} - -off_t -gbe_x_offset(size_t p, const char *f_op, const char *d_type, - off_t nsize, off_t ncmp) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - off_t off; - - check_bin(p, "part number"); - - off = ((off_t)p) * (off_t)nsize; - - if (off > ncmp - GBE_PART_SIZE) - err(ECANCELED, "%s: GbE %s %s out of bounds", - f->fname, d_type, f_op); - - if (off != 0 && off != ncmp >> 1) - err(ECANCELED, "%s: GbE %s %s at bad offset", - f->fname, d_type, f_op); - - return off; -} - -ssize_t -rw_gbe_file_exact(int fd, unsigned char *mem, size_t nrw, - off_t off, int rw_type) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - ssize_t r; - - if (io_args(fd, mem, nrw, off, rw_type) == -1) - return -1; - - if (mem != (void *)f->pad) { - if (mem < f->buf) - goto err_rw_gbe_file_exact; - - if ((size_t)(mem - f->buf) >= GBE_WORK_SIZE) - goto err_rw_gbe_file_exact; - } - - if (off < 0 || off >= f->gbe_file_size) - goto err_rw_gbe_file_exact; - - if (nrw > (size_t)(f->gbe_file_size - off)) - goto err_rw_gbe_file_exact; - - if (nrw > (size_t)GBE_PART_SIZE) - goto err_rw_gbe_file_exact; - - r = rw_file_exact(fd, mem, nrw, off, rw_type, - NO_LOOP_EAGAIN, LOOP_EINTR, MAX_ZERO_RW_RETRY, - OFF_ERR); - - return rw_over_nrw(r, nrw); - -err_rw_gbe_file_exact: - errno = EIO; - return -1; -} diff --git a/util/nvmutil/lib/mkhtemp.c b/util/nvmutil/lib/mkhtemp.c deleted file mode 100644 index 2fcb894e..00000000 --- a/util/nvmutil/lib/mkhtemp.c +++ /dev/null @@ -1,1133 +0,0 @@ -/* SPDX-License-Identifier: MIT - * Copyright (c) 2026 Leah Rowe <leah@libreboot.org> - * - * Hardened mktemp (be nice to the demon). - */ - -#if defined(__linux__) && !defined(_GNU_SOURCE) -/* for openat2 syscall on linux */ -#define _GNU_SOURCE 1 -#endif - -#include <sys/types.h> -#include <sys/stat.h> - -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -/* for openat2: */ -#ifdef __linux__ -#include <linux/openat2.h> -#include <sys/syscall.h> -#endif - -#include "../include/common.h" - -int -new_tmpfile(int *fd, char **path) -{ - return new_tmp_common(fd, path, MKHTEMP_FILE); -} - -int -new_tmpdir(int *fd, char **path) -{ - return new_tmp_common(fd, path, MKHTEMP_DIR); -} - -static int -new_tmp_common(int *fd, char **path, int type) -{ -#if defined(PATH_LEN) && \ - (PATH_LEN) >= 256 - size_t maxlen = PATH_LEN; -#else - size_t maxlen = 4096; -#endif - struct stat st; - - char suffix[] = - "tmpXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; - char *tmpdir = NULL; - - int close_errno; - size_t dirlen; - size_t destlen; - char *dest = NULL; /* final path (will be written into "path") */ - int saved_errno = errno; - int dirfd = -1; - const char *fname = NULL; - - struct stat st_dir_initial; - - if (path == NULL || fd == NULL) { - errno = EFAULT; - goto err; - } - - /* don't mess with someone elses file */ - if (*fd >= 0) { - errno = EEXIST; - goto err; - } - - /* regarding **path: - * the pointer (to the pointer) - * must nott be null, but we don't - * care about the pointer it points - * to. you should expect it to be - * replaced upon successful return - * - * (on error, it will not be touched) - */ - - - *fd = -1; - -#if defined(PERMIT_NON_STICKY_ALWAYS) && \ - ((PERMIT_NON_STICKY_ALWAYS) > 0) - tmpdir = env_tmpdir(PERMIT_NON_STICKY_ALWAYS); -#else - tmpdir = env_tmpdir(0); -#endif - if (tmpdir == NULL) - goto err; - - if (slen(tmpdir, maxlen, &dirlen) < 0) - goto err; - if (*tmpdir == '\0') - goto err; - if (*tmpdir != '/') - goto err; - - /* sizeof adds an extra byte, useful - * because we also want '.' or '/' - */ - destlen = dirlen + sizeof(suffix); - if (destlen > maxlen - 1) { - errno = EOVERFLOW; - goto err; - } - - dest = malloc(destlen + 1); - if (dest == NULL) { - errno = ENOMEM; - goto err; - } - - memcpy(dest, tmpdir, dirlen); - *(dest + dirlen) = '/'; - memcpy(dest + dirlen + 1, suffix, sizeof(suffix) - 1); - *(dest + destlen) = '\0'; - - fname = dest + dirlen + 1; - - dirfd = fs_open(tmpdir, - O_RDONLY | O_DIRECTORY); - if (dirfd < 0) - goto err; - - if (fstat(dirfd, &st_dir_initial) < 0) - goto err; - - *fd = mkhtemp(fd, &st, dest, dirfd, - fname, &st_dir_initial, type); - if (*fd < 0) - goto err; - - if (dirfd >= 0) { - close_errno = errno; - (void) close_on_eintr(dirfd); - errno = close_errno; - dirfd = -1; - } - - errno = saved_errno; - *path = dest; - - return 0; - -err: - - if (errno != saved_errno) - saved_errno = errno; - else - saved_errno = errno = EIO; - - if (dest != NULL) { - free(dest); - dest = NULL; - } - - if (dirfd >= 0) { - close_errno = errno; - (void) close_on_eintr(dirfd); - errno = close_errno; - dirfd = -1; - } - - if (*fd >= 0) { - close_errno = errno; - (void) close_on_eintr(*fd); - errno = close_errno; - *fd = -1; - } - - errno = saved_errno; - return -1; -} - - -/* hardened TMPDIR parsing - */ - -char * -env_tmpdir(int bypass_all_sticky_checks) -{ - char *t; - int allow_noworld_unsticky; - int saved_errno = errno; - - t = getenv("TMPDIR"); - - if (t != NULL && *t != '\0') { - - if (tmpdir_policy(t, - &allow_noworld_unsticky) < 0) { - errno = EPERM; - return NULL; /* errno already set */ - } - - if (!world_writeable_and_sticky(t, - allow_noworld_unsticky, - bypass_all_sticky_checks)) { - errno = EPERM; - return NULL; - } - - errno = saved_errno; - return t; - } - - allow_noworld_unsticky = 0; - - if (world_writeable_and_sticky("/tmp", - allow_noworld_unsticky, - bypass_all_sticky_checks)) { - - errno = saved_errno; - return "/tmp"; - } - - if (world_writeable_and_sticky("/var/tmp", - allow_noworld_unsticky, - bypass_all_sticky_checks)) { - - errno = saved_errno; - return "/var/tmp"; - } - - if (errno == saved_errno) - errno = EPERM; - - return NULL; -} - -int -tmpdir_policy(const char *path, - int *allow_noworld_unsticky) -{ - int saved_errno = errno; - int r; - - if (path == NULL || - allow_noworld_unsticky == NULL) { - - errno = EFAULT; - return -1; - } - - *allow_noworld_unsticky = 1; - - r = same_dir(path, "/tmp"); - if (r < 0) - goto err_tmpdir_policy; - if (r > 0) - *allow_noworld_unsticky = 0; - - r = same_dir(path, "/var/tmp"); - if (r < 0) - goto err_tmpdir_policy; - if (r > 0) - *allow_noworld_unsticky = 0; - - errno = saved_errno; - return 0; - -err_tmpdir_policy: - - if (errno == saved_errno) - errno = EIO; - - return -1; -} - -int -same_dir(const char *a, const char *b) -{ - int fd_a = -1; - int fd_b = -1; - - struct stat st_a; - struct stat st_b; - - int saved_errno = errno; - int rval_scmp; - -#if defined(PATH_LEN) && \ - (PATH_LEN) >= 256 - size_t maxlen = (PATH_LEN); -#else - size_t maxlen = 4096; -#endif - - /* optimisation: if both dirs - are the same, we don't need - to check anything. sehr schnell: - */ - if (scmp(a, b, maxlen, &rval_scmp) < 0) - goto err_same_dir; - /* bonus: scmp checks null for us */ - if (rval_scmp == 0) - goto success_same_dir; - - fd_a = fs_open(a, O_RDONLY | O_DIRECTORY); - if (fd_a < 0) - goto err_same_dir; - - fd_b = fs_open(b, O_RDONLY | O_DIRECTORY); - if (fd_b < 0) - goto err_same_dir; - - if (fstat(fd_a, &st_a) < 0) - goto err_same_dir; - - if (fstat(fd_b, &st_b) < 0) - goto err_same_dir; - - if (st_a.st_dev == st_b.st_dev && - st_a.st_ino == st_b.st_ino) { - - (void) close_on_eintr(fd_a); - (void) close_on_eintr(fd_b); - -success_same_dir: - - /* SUCCESS - */ - - errno = saved_errno; - return 1; - } - - (void) close_on_eintr(fd_a); - (void) close_on_eintr(fd_b); - - /* FAILURE (logical) - */ - - errno = saved_errno; - return 0; - -err_same_dir: - - /* FAILURE (probably syscall) - */ - - if (fd_a >= 0) - (void) close_on_eintr(fd_a); - if (fd_b >= 0) - (void) close_on_eintr(fd_b); - - if (errno == saved_errno) - errno = EIO; - - return -1; -} - -/* bypass_all_sticky_checks: if set, - disable stickiness checks (libc behaviour) - (if not set: leah behaviour) - - allow_noworld_unsticky: - allow non-sticky files if not world-writeable - (still block non-sticky in standard TMPDIR) -*/ -int -world_writeable_and_sticky( - const char *s, - int allow_noworld_unsticky, - int bypass_all_sticky_checks) -{ - struct stat st; - int dirfd = -1; - - int saved_errno = errno; - - if (s == NULL || *s == '\0') { - errno = EINVAL; - goto sticky_hell; - } - - /* mitigate symlink attacks* - */ - dirfd = fs_open(s, O_RDONLY | O_DIRECTORY); - if (dirfd < 0) - goto sticky_hell; - - if (fstat(dirfd, &st) < 0) - goto sticky_hell; - - if (!S_ISDIR(st.st_mode)) { - errno = ENOTDIR; - goto sticky_hell; - } - - /* must be fully executable - * by everyone, or openat2 - * becomes unreliable** - */ - if (!(st.st_mode & S_IXUSR) || - !(st.st_mode & S_IXGRP) || - !(st.st_mode & S_IXOTH)) { - - errno = EACCES; - goto sticky_hell; - } - - /* *normal-**ish mode (libc): - */ - - if (bypass_all_sticky_checks) - goto sticky_heaven; /* normal == no security */ - - /* unhinged leah mode: - */ - - if (st.st_mode & S_IWOTH) { /* world writeable */ - - /* if world-writeable, only - * allow sticky files - */ - if (st.st_mode & S_ISVTX) - goto sticky_heaven; /* sticky */ - - errno = EPERM; - goto sticky_hell; /* not sticky */ - } - - /* non-world-writeable, so - * stickiness is do-not-care - */ - if (allow_noworld_unsticky) - goto sticky_heaven; /* sticky! */ - - goto sticky_hell; /* heaven visa denied */ - -sticky_heaven: -/* i like the one in hamburg better */ - - if (dirfd >= 0) - (void) close_on_eintr(dirfd); - - errno = saved_errno; - - return 1; - -sticky_hell: - - if (errno == saved_errno) - errno = EPERM; - - saved_errno = errno; - - if (dirfd >= 0) - (void) close_on_eintr(dirfd); - - errno = saved_errno; - - return 0; -} - -/* mk(h)temp - hardened mktemp. - * like mkstemp, but (MUCH) harder. - * - * designed to resist TOCTOU attacsk - * e.g. directory race / symlink attack - * - * extremely strict and even implements - * some limited userspace-level sandboxing, - * similar to openbsd unveil (which you - * can also use with this in your program) - * - * supports both files and directories. - * file: type = MKHTEMP_FILE (0) - * dir: type = MKHTEMP_DIR (1) - * - * DESIGN NOTES: - * - * caller is expected to handle - * cleanup e.g. free(), on *st, - * *template, *fname (all of the - * pointers). ditto fd cleanup. - * - * some limited cleanup is - * performed here, e.g. directory/file - * cleanup on error in mkhtemp_try_create - * - * we only check if these are not NULL, - * and the caller is expected to take - * care; without too many conditions, - * these functions are more flexible, - * but some precauttions are taken: - * - * when used via the function new_tmpfile - * or new_tmpdir, thtis is extremely strict, - * much stricter than previous mktemp - * variants. for example, it is much - * stricter about stickiness on world - * writeable directories, and it enforces - * file ownership under hardened mode - * (only lets you touch your own files/dirs) - */ -int -mkhtemp(int *fd, - struct stat *st, - char *template, - int dirfd, - const char *fname, - struct stat *st_dir_initial, - int type) -{ - size_t len = 0; - size_t xc = 0; - size_t fname_len = 0; - - char *fname_copy = NULL; - char *p; - - size_t retries; - - int close_errno; - int saved_errno = errno; - -#if defined(PATH_LEN) && \ - (PATH_LEN) >= 256 - size_t max_len = PATH_LEN; -#else - size_t max_len = 4096; -#endif - int r; - char *end; - - if (fd == NULL || - template == NULL || - fname == NULL || - st_dir_initial == NULL) { - - errno = EFAULT; - return -1; - } - - /* we do not mess with an - open descriptor. - */ - if (*fd >= 0) { - errno = EEXIST; /* leave their file alone */ - return -1; - } - - if (dirfd < 0) { - errno = EBADF; - return -1; - } - - if (slen(template, max_len, &len) < 0) - return -1; - - if (len >= max_len) { - errno = EMSGSIZE; - return -1; - } - - if (slen(fname, max_len, &fname_len) < 0) - return -1; - - if (fname_len == 0) { - errno = EINVAL; - return -1; - } - - if (strrchr(fname, '/') != NULL) { - errno = EINVAL; - return -1; - } - - /* count trailing X */ - end = template + len; - while (end > template && *--end == 'X') - xc++; - - if (xc < 12 || xc > len) { - errno = EINVAL; - return -1; - } - - if (fname_len > len) { - errno = EOVERFLOW; - return -1; - } - - if (memcmp(fname, - template + len - fname_len, - fname_len) != 0) { - errno = EINVAL; - return -1; - } - - fname_copy = malloc(fname_len + 1); - if (fname_copy == NULL) { - errno = ENOMEM; - goto err; - } - - /* fname_copy = suffix region only; p points to trailing XXXXXX */ - memcpy(fname_copy, - template + len - fname_len, - fname_len + 1); - p = fname_copy + fname_len - xc; - - for (retries = 0; retries < MKHTEMP_RETRY_MAX; retries++) { - - r = mkhtemp_try_create( - dirfd, - st_dir_initial, - fname_copy, - p, - xc, - fd, - st, - type); - - if (r == 0) { - if (retries >= MKHTEMP_SPIN_THRESHOLD) { - /* usleep can return EINTR */ - close_errno = errno; - usleep((useconds_t)rlong() & 0x3FF); - errno = close_errno; - } - continue; - } - if (r < 0) - goto err; - - /* success: copy final name back */ - memcpy(template + len - fname_len, - fname_copy, fname_len); - - errno = saved_errno; - goto success; - } - - errno = EEXIST; - -err: - if (*fd >= 0) { - close_errno = errno; - (void)close_on_eintr(*fd); - errno = close_errno; - *fd = -1; - } - -success: - - if (fname_copy != NULL) - free(fname_copy); - - return (*fd >= 0) ? *fd : -1; -} - -static int -mkhtemp_try_create(int dirfd, - struct stat *st_dir_initial, - char *fname_copy, - char *p, - size_t xc, - int *fd, - struct stat *st, - int type) -{ - struct stat st_open; - int saved_errno = errno; - int close_errno; - int rval = -1; - - int file_created = 0; - int dir_created = 0; - - if (fd == NULL || st == NULL || p == NULL || fname_copy == NULL || - st_dir_initial == NULL) { - errno = EFAULT; - goto err; - } else if (*fd >= 0) { /* do not mess with someone else's file */ - errno = EEXIST; - goto err; - } - - if (mkhtemp_fill_random(p, xc) < 0) - goto err; - - if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0) - goto err; - - if (type == MKHTEMP_FILE) { - *fd = openat2p(dirfd, fname_copy, - O_RDWR | O_CREAT | O_EXCL | - O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, - 0600); - - /* O_CREAT and O_EXCL guarantees - * creation upon success - */ - if (*fd >= 0) - file_created = 1; - - } else { /* dir: MKHTEMP_DIR */ - - if (mkdirat_on_eintr(dirfd, fname_copy, 0700) < 0) - goto err; - - /* ^ NOTE: opening the directory here - will never set errno=EEXIST, - since we're not creating it */ - - dir_created = 1; - - /* do it again (mitigate directory race) */ - if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0) - goto err; - - *fd = openat2p(dirfd, fname_copy, - O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0); - if (*fd < 0) - goto err; - - if (fstat(*fd, &st_open) < 0) - goto err; - - if (!S_ISDIR(st_open.st_mode)) { - errno = ENOTDIR; - goto err; - } - - /* NOTE: could check nlink count here, - * but it's not very reliable here. skipped. - */ - - if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0) - goto err; - - } - - /* NOTE: openat2p and mkdirat_on_eintr - * already handled EINTR/EAGAIN looping - */ - - if (*fd < 0) { - if (errno == EEXIST) { - - rval = 0; - goto out; - } - goto err; - } - - if (fstat(*fd, &st_open) < 0) - goto err; - - if (type == MKHTEMP_FILE) { - - if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0) - goto err; - - if (secure_file(fd, st, &st_open, - O_APPEND, 1, 1, 0600) < 0) /* WARNING: only once */ - goto err; - - } else { /* dir: MKHTEMP_DIR */ - - if (fd_verify_identity(*fd, &st_open, st_dir_initial) < 0) - goto err; - - if (!S_ISDIR(st_open.st_mode)) { - errno = ENOTDIR; - goto err; - } - - if (is_owner(&st_open) < 0) - goto err; - - /* group/world writeable */ - if (st_open.st_mode & (S_IWGRP | S_IWOTH)) { - errno = EPERM; - goto err; - } - } - - errno = saved_errno; - rval = 1; - goto out; - -err: - close_errno = errno; - - if (fd != NULL && *fd >= 0) { - (void) close_on_eintr(*fd); - *fd = -1; - } - - if (file_created) - (void) unlinkat(dirfd, fname_copy, 0); - - if (dir_created) - (void) unlinkat(dirfd, fname_copy, AT_REMOVEDIR); - - errno = close_errno; - rval = -1; -out: - return rval; -} - -int -mkhtemp_fill_random(char *p, size_t xc) -{ - size_t chx = 0; - int rand_failures = 0; - - size_t r; - - int saved_rand_error = 0; - static char ch[] = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - - /* clamp rand to prevent modulo bias - * (reduced risk of entropy leak) - */ - size_t limit = ((size_t)-1) - (((size_t)-1) % (sizeof(ch) - 1)); - - int saved_errno = errno; - - if (p == NULL) { - errno = EFAULT; - goto err_mkhtemp_fill_random; - } - - for (chx = 0; chx < xc; chx++) { - - do { - saved_rand_error = errno; - rand_failures = 0; -retry_rand: - errno = 0; - - /* on bsd: uses arc4random - on linux: uses getrandom - on OLD linux: /dev/urandom - on old/other unix: /dev/urandom - */ - r = rlong(); - - if (errno > 0) { - if (++rand_failures <= 8) - goto retry_rand; - - goto err_mkhtemp_fill_random; - } - - rand_failures = 0; - errno = saved_rand_error; - - } while (r >= limit); - - p[chx] = ch[r % (sizeof(ch) - 1)]; - } - - errno = saved_errno; - return 0; - -err_mkhtemp_fill_random: - - if (errno == saved_errno) - errno = ECANCELED; - - return -1; -} - -/* WARNING: **ONCE** per file. - * - * !!! DO NOT RUN TWICE PER FILE. BEWARE OF THE DEMON !!! - * watch out for spikes! - */ -int secure_file(int *fd, - struct stat *st, - struct stat *expected, - int bad_flags, - int check_seek, - int do_lock, - mode_t mode) -{ - int flags; - struct stat st_now; - int saved_errno = errno; - /* you have been warned */ - if (fd == NULL) { - errno = EFAULT; - goto err_demons; - } - if (*fd < 0) { - errno = EBADF; - goto err_demons; - } - - if (st == NULL) { - errno = EFAULT; - goto err_demons; - } - - flags = fcntl(*fd, F_GETFL); - - if (flags == -1) - goto err_demons; - - if (bad_flags > 0) { - - /* e.g. O_APPEND breaks pwrite/pread - * by allowing offsets to be ignored */ - if (flags & bad_flags) { - errno = EPERM; - goto err_demons; - } - } - - if (expected != NULL) { - if (fd_verify_regular(*fd, expected, st) < 0) - goto err_demons; - } else { - if (fstat(*fd, &st_now) == -1) - goto err_demons; - - if (!S_ISREG(st_now.st_mode)) { - errno = EBADF; - goto err_demons; - } - - *st = st_now; - } - - if (check_seek) { - - /* check if it's seekable */ - if (lseek(*fd, 0, SEEK_CUR) == (off_t)-1) - goto err_demons; - } - - /* don't release the demon - */ - if (st->st_nlink != 1) { /***********/ - /* ( >:3 ) */ - errno = ELOOP; /* /| |\ */ /* don't let him out */ - goto err_demons; /* / \ */ - /***********/ - } - - if (st->st_uid != geteuid() && /* someone else's file */ - geteuid() != 0) { /* override for root */ - - errno = EPERM; - goto err_demons; - } - if (is_owner(st) < 0) - goto err_demons; - - /* world-writeable or group-ownership. - * if these are set, then others could - * modify the file (not secure) - */ - if (st->st_mode & (S_IWGRP | S_IWOTH)) { - errno = EPERM; - goto err_demons; - } - - if (do_lock) { - if (lock_file(*fd, flags) == -1) - goto err_demons; - - if (expected != NULL) { - if (fd_verify_identity(*fd, expected, &st_now) < 0) - goto err_demons; - } - } - - if (fchmod(*fd, mode) == -1) - goto err_demons; - - errno = saved_errno; - return 0; - -err_demons: - - if (errno == saved_errno) - errno = EIO; - - return -1; -} - -int -fd_verify_regular(int fd, - const struct stat *expected, - struct stat *out) -{ - if (fd_verify_identity(fd, expected, out) < 0) - return -1; - - if (!S_ISREG(out->st_mode)) { - errno = EBADF; - return -1; - } - - return 0; /* regular file */ -} - -int -fd_verify_identity(int fd, - const struct stat *expected, - struct stat *out) -{ - struct stat st_now; - int saved_errno = errno; - - if (fd < 0 || expected == NULL) { - errno = EFAULT; - return -1; - } - - if (fstat(fd, &st_now) < 0) - return -1; - - if (st_now.st_dev != expected->st_dev || - st_now.st_ino != expected->st_ino) { - errno = ESTALE; - return -1; - } - - if (out != NULL) - *out = st_now; - - errno = saved_errno; - return 0; -} - -int -fd_verify_dir_identity(int fd, - const struct stat *expected) -{ - struct stat st_now; - int saved_errno = errno; - - if (fd < 0 || expected == NULL) { - errno = EFAULT; - return -1; - } - - if (fstat(fd, &st_now) < 0) - return -1; - - if (st_now.st_dev != expected->st_dev || - st_now.st_ino != expected->st_ino) { - errno = ESTALE; - return -1; - } - - if (!S_ISDIR(st_now.st_mode)) { - errno = ENOTDIR; - return -1; - } - - errno = saved_errno; - return 0; -} - -int -is_owner(struct stat *st) -{ - if (st == NULL) { - - errno = EFAULT; - return -1; - } - -#if ALLOW_ROOT_OVERRIDE - if (st->st_uid != geteuid() && /* someone else's file */ - geteuid() != 0) { /* override for root */ -#else - if (st->st_uid != geteuid()) { /* someone else's file */ -#endif /* and no root override */ - errno = EPERM; - return -1; - } - - return 0; -} - -int -lock_file(int fd, int flags) -{ - struct flock fl; - int saved_errno = errno; - - if (fd < 0) { - errno = EBADF; - goto err_lock_file; - } - - if (flags < 0) { - errno = EINVAL; - goto err_lock_file; - } - - memset(&fl, 0, sizeof(fl)); - - if ((flags & O_ACCMODE) == O_RDONLY) - fl.l_type = F_RDLCK; - else - fl.l_type = F_WRLCK; - - fl.l_whence = SEEK_SET; - - if (fcntl(fd, F_SETLK, &fl) == -1) - goto err_lock_file; - - saved_errno = errno; - return 0; - -err_lock_file: - - if (errno == saved_errno) - errno = EIO; - - return -1; -} diff --git a/util/nvmutil/lib/num.c b/util/nvmutil/lib/num.c deleted file mode 100644 index 343350b0..00000000 --- a/util/nvmutil/lib/num.c +++ /dev/null @@ -1,444 +0,0 @@ -/* SPDX-License-Identifier: MIT - * Copyright (c) 2026 Leah Rowe <leah@libreboot.org> - * - * Numerical functions. - */ - -/* -TODO: properly handle errno in this file - */ - -#ifdef __OpenBSD__ -#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) || \ - defined(__FreeBSD__) || \ - defined(__NetBSD__) || defined(__APPLE__)) -#include <fcntl.h> /* if not arc4random: /dev/urandom */ -#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" - -/* TODO: - * make this and errno handling more - * flexible - - in particular: - hextonum could be modified to - write into a buffer instead, - with the converted numbers, - of an arbitrary length - */ -unsigned short -hextonum(char ch_s) -{ - int saved_errno = errno; - - /* rlong() can return error, - but preserves errno if no - error. we need to detect - this because it handles - /dev/urandom sometimes - - therefore, if it's zero - at start, we know if there - was an err at the end, by - return value zero, if errno - was set; this is technically - valid, since zero is also - a valid random number! - - it's an edge case that i had - to fix. i'll rewrite the code - better later. for now, it - should be ok. - */ - errno = 0; - - unsigned char ch; - size_t rval; - - ch = (unsigned char)ch_s; - - if ((unsigned int)(ch - '0') <= 9) { - - rval = ch - '0'; - goto hextonum_success; - } - - ch |= 0x20; - - if ((unsigned int)(ch - 'a') <= 5) { - - rval = ch - 'a' + 10; - goto hextonum_success; - } - - if (ch == '?' || ch == 'x') { - - rval = rlong(); - if (errno > 0) - goto err_hextonum; - - goto hextonum_success; - } - - goto err_hextonum; - -hextonum_success: - - errno = saved_errno; - return (unsigned short)rval & 0xf; - -err_hextonum: - - if (errno == saved_errno) - errno = EINVAL; - else - return 17; /* 17 indicates getrandom/urandom fail */ - - return 16; /* invalid character */ - - /* caller just checks >15. */ -} - -/* Random numbers - */ - -/* when calling this: save errno - * first, then set errno to zero. - * on error, this function will - * set errno and possibly return - * - * rlong also preserves errno - * and leaves it unchanged on - * success, so if you do it - * right, you can detect error. - * this is because it uses - * /dev/urandom which can err. - * ditto getrandom (EINTR), - * theoretically. - */ - -size_t -rlong(void) -{ -#if !(defined(FALLBACK_RAND_1989) && \ - ((FALLBACK_RAND_1989) > 0)) -#if (defined(__OpenBSD__) && (OpenBSD) >= 201) || \ - defined(__FreeBSD__) || \ - defined(__NetBSD__) || defined(__APPLE__) - - int saved_errno = errno; - size_t rval; - - arc4random_buf(&rval, sizeof(size_t)); - - 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; - - 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; - - /* to mitigate file descriptor - * injection, we do not re-use - * the same descriptor each time - */ - (void) close_on_eintr(fd); - fd = -1; - } - - 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; - - if (!len) { - errno = EINVAL; - return -1; - } - - if (buf == NULL) { - errno = EFAULT; - return -1; - } - -#if defined(HAVE_GETRANDOM) || \ - defined(HAVE_GETRANDOM_SYSCALL) - - 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 - - if (rval < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - - errno = EIO; - return -1; /* unsupported by kernel */ - } - - if (rval == 0) { - errno = EIO; - return -1; - } - - off += (size_t)rval; - } - - errno = saved_errno; - return 0; - -#else - (void)buf; - (void)len; - - errno = EIO; - return -1; -#endif -} -#endif -#endif -#else -size_t -fallback_rand_1989(void) -{ - static size_t mix = 0; - static size_t counter = 0; - - struct timeval tv; - - /* nobody should use this - * (not crypto-safe) - */ - - gettimeofday(&tv, NULL); - - mix ^= (size_t)tv.tv_sec - ^ (size_t)tv.tv_usec - ^ (size_t)getpid() - ^ (size_t)&mix - ^ counter++ - ^ entropy_jitter(); - - /* - * Stack addresses can vary between - * calls, thus increasing entropy. - */ - mix ^= (size_t)&mix; - mix ^= (size_t)&tv; - mix ^= (size_t)&counter; - - return mix; -} - -size_t -entropy_jitter(void) -{ - size_t mix; - - struct timeval a, b; - ssize_t 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 = (ssize_t)(b.tv_usec - a.tv_usec); - if (mix_diff < 0) - mix_diff = -mix_diff; - - mix ^= (size_t)(mix_diff); - - mix ^= (size_t)&mix; - - } - - return mix; -} -#endif - -void -check_bin(size_t a, const char *a_name) -{ - if (a > 1) - err(EINVAL, "%s must be 0 or 1, but is %lu", - a_name, (size_t)a); -} diff --git a/util/nvmutil/lib/state.c b/util/nvmutil/lib/state.c deleted file mode 100644 index 19d5cd8c..00000000 --- a/util/nvmutil/lib/state.c +++ /dev/null @@ -1,279 +0,0 @@ -/* SPDX-License-Identifier: MIT - * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org> - * - * State machine (singleton) for nvmutil data. - */ - -#ifdef __OpenBSD__ -#include <sys/param.h> -#endif -#include <sys/types.h> -#include <sys/stat.h> - -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "../include/common.h" - -struct xstate * -xstart(int argc, char *argv[]) -{ - static int first_run = 1; - static int pre_init = 0; - - static struct xstate us = { - /* DO NOT MESS THIS UP, OR THERE WILL BE DEMONS */ - { - { - CMD_DUMP, "dump", cmd_helper_dump, ARGC_3, - ARG_NOPART, - SKIP_CHECKSUM_READ, SKIP_CHECKSUM_WRITE, - NVM_SIZE, O_RDONLY - }, { - CMD_SETMAC, "setmac", cmd_helper_setmac, ARGC_3, - ARG_NOPART, - CHECKSUM_READ, CHECKSUM_WRITE, - NVM_SIZE, O_RDWR - }, { - CMD_SWAP, "swap", cmd_helper_swap, ARGC_3, - ARG_NOPART, - CHECKSUM_READ, SKIP_CHECKSUM_WRITE, - GBE_PART_SIZE, O_RDWR - }, { - CMD_COPY, "copy", cmd_helper_copy, ARGC_4, - ARG_PART, - CHECKSUM_READ, SKIP_CHECKSUM_WRITE, - GBE_PART_SIZE, O_RDWR - }, { - CMD_CAT, "cat", cmd_helper_cat, ARGC_3, - ARG_NOPART, - CHECKSUM_READ, SKIP_CHECKSUM_WRITE, - GBE_PART_SIZE, O_RDONLY - }, { - CMD_CAT16, "cat16", cmd_helper_cat16, ARGC_3, - ARG_NOPART, - CHECKSUM_READ, SKIP_CHECKSUM_WRITE, - GBE_PART_SIZE, O_RDONLY - }, { - CMD_CAT128, "cat128", cmd_helper_cat128, ARGC_3, - ARG_NOPART, - CHECKSUM_READ, SKIP_CHECKSUM_WRITE, - GBE_PART_SIZE, O_RDONLY - } - }, - - /* ->mac */ - {NULL, "xx:xx:xx:xx:xx:xx", {0, 0, 0}}, /* .str, .rmac, .mac_buf */ - - /* .f */ - {0}, - - /* .argv0 (for our getprogname implementation) */ - NULL, - - /* ->i (index to cmd[]) */ - 0, - - /* .no_cmd (set 0 when a command is found) */ - 1, - - /* .cat (cat helpers set this) */ - -1 - - }; - - if (!first_run) { - if (pre_init) - err_no_cleanup(ECANCELED, - "Outside access to state during init"); - - first_run = 0; - - return &us; - } - - if (argc < 3) - err_no_cleanup(EINVAL, "xstart: Too few arguments"); - if (argv == NULL) - err_no_cleanup(EINVAL, "xstart: NULL argv"); - - first_run = 0; - pre_init = 1; - - us.f.buf = us.f.real_buf; - - us.argv0 = argv[0]; - us.f.fname = argv[1]; - - if (new_tmpfile(&us.f.tmp_fd, &us.f.tname) < 0) - err_no_cleanup(errno, "xstart: cannot create tmpfile"); - - /* parse user command */ -/* TODO: CHECK ACCESSES VIA xstatus() */ - set_cmd(argc, argv); - set_cmd_args(argc, argv); - - if (us.f.tname == NULL) - err_no_cleanup(errno, "x->f.tname null"); - if (*us.f.tname == '\0') - err_no_cleanup(errno, "x->f.tname empty"); - - if (fstat(us.f.tmp_fd, &us.f.tmp_st) < 0) - err_no_cleanup(errno, "%s: stat", us.f.tname); - - memset(us.f.real_buf, 0, sizeof(us.f.real_buf)); - memset(us.f.bufcmp, 0, sizeof(us.f.bufcmp)); - - /* for good measure */ - memset(us.f.pad, 0, sizeof(us.f.pad)); - - pre_init = 0; - - return &us; -} - -struct xstate * -xstatus(void) -{ - struct xstate *x = xstart(0, NULL); - - if (x == NULL) - err_no_cleanup(EACCES, "NULL pointer to xstate"); - - sanitize_command_list(); - - return x; -} - -/* early init functions that - should not access state - WARNING: - does not do cleanup. only - call this during pre-init - */ -void -err_no_cleanup(int nvm_errval, const char *msg, ...) -{ - va_list args; - - if (errno == 0) - errno = nvm_errval; - if (!errno) - errno = ECANCELED; - - fprintf(stderr, "nvmutil: "); - - va_start(args, msg); - vfprintf(stderr, msg, args); - va_end(args); - - fprintf(stderr, ": %s\n", strerror(errno)); - - exit(EXIT_FAILURE); -} - -void -err(int nvm_errval, const char *msg, ...) -{ - struct xstate *x = xstatus(); - - va_list args; - - if (errno == 0) - errno = nvm_errval; - if (!errno) - errno = ECANCELED; - - (void)exit_cleanup(); - - if (x != NULL) - fprintf(stderr, "%s: ", getnvmprogname()); - - va_start(args, msg); - vfprintf(stderr, msg, args); - va_end(args); - - fprintf(stderr, ": %s\n", strerror(errno)); - - exit(EXIT_FAILURE); -} - -const char * -getnvmprogname(void) -{ - struct xstate *x = xstatus(); - - const char *p; - static char fallback[] = "nvmutil"; - - char *rval = fallback; - - if (x != NULL) { - if (x->argv0 == NULL || *x->argv0 == '\0') - return ""; - - rval = x->argv0; - } - - p = strrchr(rval, '/'); - - if (p) - return p + 1; - else - return rval; -} - -int -exit_cleanup(void) -{ - struct xstate *x = xstatus(); - struct xfile *f; - - int close_err; - int saved_errno; - - close_err = 0; - saved_errno = errno; - - if (x != NULL) { - f = &x->f; - - if (f->gbe_fd > -1) { - if (close_on_eintr(f->gbe_fd) == -1) { - f->gbe_fd = -1; - close_err = 1; - } - f->gbe_fd = -1; - } - - if (f->tmp_fd > -1) { - if (close_on_eintr(f->tmp_fd) == -1) { - f->tmp_fd = -1; - close_err = 1; - } - f->tmp_fd = -1; - } - - if (f->tname != NULL) { - if (unlink(f->tname) == -1) - close_err = 1; - } - - f->tmp_fd = -1; - } - - if (saved_errno) - errno = saved_errno; - - if (close_err) - return -1; - - return 0; -} diff --git a/util/nvmutil/lib/string.c b/util/nvmutil/lib/string.c deleted file mode 100644 index ca58fb1c..00000000 --- a/util/nvmutil/lib/string.c +++ /dev/null @@ -1,114 +0,0 @@ -/* SPDX-License-Identifier: MIT - * Copyright (c) 2026 Leah Rowe <leah@libreboot.org> - * - * String functions - */ - -#include <sys/types.h> -#include <sys/stat.h> - -#include <errno.h> -#include <stddef.h> -#include <string.h> -#include <unistd.h> - -#include "../include/common.h" - -/* scmp() - strict string comparison - * - * strict string comparison - * similar to strncmp, but null and - * unterminated inputs do not produce - * a return value; on error, errno is - * set and -1 is returned. - * - * the real return value is stored in - * the 4th argument by pointer. - * - * the value at rval pointer is set, - * only upon success. callers should - * check the return value accordingly. - */ - -int -scmp(const char *a, - const char *b, - size_t maxlen, - int *rval) -{ - size_t ch; - unsigned char ac; - unsigned char bc; - - if (a == NULL || - b == NULL || - rval == NULL) { - - errno = EFAULT; - return -1; - } - - for (ch = 0; ch < maxlen; ch++) { - - ac = (unsigned char)a[ch]; - bc = (unsigned char)b[ch]; - - if (ac != bc) { - *rval = ac - bc; - return 0; - } - - if (ac == '\0') { - *rval = 0; - return 0; - } - } - - /* block unterminated strings */ - errno = EFAULT; - return -1; -} - -/* slen() - strict strict length - * - * strict string length calculation - * similar to strnlen, but null and - * unterminated inputs do not produce - * a return value; on error, errno is - * set and -1 is returned. - * - * the real return value is stored in - * the 3rd argument by pointer. - * - * the value at rval pointer is set, - * only upon success. callers should - * check the return value accordingly. - */ - -int -slen(const char *s, - size_t maxlen, - size_t *rval) -{ - size_t ch; - - if (s == NULL || - rval == NULL) { - - errno = EFAULT; - return -1; - } - - for (ch = 0; - ch < maxlen && s[ch] != '\0'; - ch++); - - if (ch == maxlen) { - /* unterminated */ - errno = EFAULT; - return -1; - } - - *rval = ch; - return 0; -} diff --git a/util/nvmutil/lib/usage.c b/util/nvmutil/lib/usage.c deleted file mode 100644 index 3b0614e8..00000000 --- a/util/nvmutil/lib/usage.c +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: MIT - * Copyright (c) 2023 Riku Viitanen <riku.viitanen@protonmail.com> - * Copyright (c) 2026 Leah Rowe <leah@libreboot.org> - */ - -#include <errno.h> -#include <stdio.h> - -#include "../include/common.h" - -void -usage(void) -{ - const char *util = getnvmprogname(); - - fprintf(stderr, - "Modify Intel GbE NVM images e.g. set MAC\n" - "USAGE:\n" - "\t%s FILE dump\n" - "\t%s FILE setmac [MAC]\n" - "\t%s FILE swap\n" - "\t%s FILE copy 0|1\n" - "\t%s FILE cat\n" - "\t%s FILE cat16\n" - "\t%s FILE cat128\n", - util, util, util, util, - util, util, util); - - err(EINVAL, "Too few arguments"); -} diff --git a/util/nvmutil/lib/word.c b/util/nvmutil/lib/word.c deleted file mode 100644 index f84dae6a..00000000 --- a/util/nvmutil/lib/word.c +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: MIT - * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org> - * - * Manipulate Intel GbE NVM words, which are 16-bit little - * endian in the files (MAC address words are big endian). - */ - -#include <sys/types.h> - -#include <errno.h> -#include <stddef.h> - -#include "../include/common.h" - -unsigned short -nvm_word(size_t pos16, size_t p) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - size_t pos; - - check_nvm_bound(pos16, p); - pos = (pos16 << 1) + (p * GBE_PART_SIZE); - - return (unsigned short)f->buf[pos] | - ((unsigned short)f->buf[pos + 1] << 8); -} - -void -set_nvm_word(size_t pos16, size_t p, unsigned short val16) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - size_t pos; - - check_nvm_bound(pos16, p); - pos = (pos16 << 1) + (p * GBE_PART_SIZE); - - f->buf[pos] = (unsigned char)(val16 & 0xff); - f->buf[pos + 1] = (unsigned char)(val16 >> 8); - - set_part_modified(p); -} - -void -set_part_modified(size_t p) -{ - struct xstate *x = xstatus(); - struct xfile *f = &x->f; - - check_bin(p, "part number"); - f->part_modified[p] = 1; -} - -void -check_nvm_bound(size_t c, size_t p) -{ - /* Block out of bound NVM access - */ - - check_bin(p, "part number"); - - if (c >= NVM_WORDS) - err(ECANCELED, "check_nvm_bound: out of bounds %lu", - (size_t)c); -} |
