diff options
Diffstat (limited to 'util')
| -rw-r--r-- | util/libreboot-utils/include/common.h | 24 | ||||
| -rw-r--r-- | util/libreboot-utils/lib/checksum.c | 4 | ||||
| -rw-r--r-- | util/libreboot-utils/lib/command.c | 58 | ||||
| -rw-r--r-- | util/libreboot-utils/lib/file.c | 187 | ||||
| -rw-r--r-- | util/libreboot-utils/lib/io.c | 102 | ||||
| -rw-r--r-- | util/libreboot-utils/lib/mkhtemp.c | 110 | ||||
| -rw-r--r-- | util/libreboot-utils/lib/num.c | 42 | ||||
| -rw-r--r-- | util/libreboot-utils/lib/rand.c | 43 | ||||
| -rw-r--r-- | util/libreboot-utils/lib/state.c | 92 | ||||
| -rw-r--r-- | util/libreboot-utils/lib/string.c | 159 | ||||
| -rw-r--r-- | util/libreboot-utils/lib/usage.c | 2 | ||||
| -rw-r--r-- | util/libreboot-utils/lib/word.c | 2 | ||||
| -rw-r--r-- | util/libreboot-utils/lottery.c | 135 | ||||
| -rw-r--r-- | util/libreboot-utils/mkhtemp.c | 51 | ||||
| -rw-r--r-- | util/libreboot-utils/nvmutil.c | 101 |
15 files changed, 508 insertions, 604 deletions
diff --git a/util/libreboot-utils/include/common.h b/util/libreboot-utils/include/common.h index 77672846..1932b2da 100644 --- a/util/libreboot-utils/include/common.h +++ b/util/libreboot-utils/include/common.h @@ -225,10 +225,12 @@ int fchmod(int fd, mode_t mode); /* command table */ +typedef void (*func_t)(void); + struct commands { size_t chk; char *str; - void (*run)(void); + func_t run; int argc; unsigned char arg_part; unsigned char chksum_read; @@ -344,7 +346,6 @@ int fd_verify_dir_identity(int fd, int is_owner(struct stat *st); int lock_file(int fd, int flags); int same_file(int fd, struct stat *st_old, int check_size); -void xopen(int *fd, const char *path, int flags, struct stat *st); /* Read GbE file and verify checksums */ @@ -375,6 +376,8 @@ void write_mac_part(size_t partnum); int xunveilx(const char *path, const char *permissions); int xpledgex(const char *promises, const char *execpromises); +char *smalloc(char **buf, size_t size); +void *vmalloc(void **buf, size_t size); int slen(const char *scmp, size_t maxlen, size_t *rval); int scmp(const char *a, const char *b, @@ -393,7 +396,6 @@ int dcat(const char *s, size_t n, unsigned short hextonum(char ch_s); void *mkrbuf(size_t n); -void *rmalloc(size_t *size); /* don't ever use this */ void rset(void *buf, size_t n); void *mkrbuf(size_t n); char *mkrstr(size_t n); @@ -487,12 +489,12 @@ int try_err(int loop_err, int errval); */ void usage(void); -void err_no_cleanup(int stfu, int nvm_errval, const char *msg, ...); -void b0rk(int nvm_errval, const char *msg, ...); -int exit_cleanup(void); +int set_errno(int saved_errno, int fallback); +void err_exit(int nvm_errval, const char *msg, ...); +func_t errhook(func_t ptr); /* hook function for cleanup on err */ const char *getnvmprogname(void); - -void err_mkhtemp(int stfu, int errval, const char *msg, ...); +void no_op(void); +void err_mkhtemp(int errval, const char *msg, ...); /* libc hardening */ @@ -537,14 +539,14 @@ int secure_file(int *fd, int check_seek, int do_lock, mode_t mode); -int close_on_eintr(int fd); +void close_on_eintr(int *fd); int fsync_on_eintr(int fd); int fs_rename_at(int olddirfd, const char *old, int newdirfd, const char *new); int fs_open(const char *path, int flags); -void close_no_err(int *fd); void free_and_set_null(char **buf); -int close_warn(int *fd, char *s); +void open_on_eintr(const char *path, int *fd, int flags, mode_t mode, + struct stat *st); struct filesystem *rootfs(void); int fs_resolve_at(int dirfd, const char *path, int flags); int fs_next_component(const char **p, diff --git a/util/libreboot-utils/lib/checksum.c b/util/libreboot-utils/lib/checksum.c index 9a041989..97b0efca 100644 --- a/util/libreboot-utils/lib/checksum.c +++ b/util/libreboot-utils/lib/checksum.c @@ -59,10 +59,10 @@ read_checksums(void) if (_num_invalid >= _max_invalid) { if (_max_invalid == 1) - b0rk(ECANCELED, "%s: part %lu has a bad checksum", + err_exit(ECANCELED, "%s: part %lu has a bad checksum", f->fname, (size_t)f->part); - b0rk(ECANCELED, "%s: No valid checksum found in file", + err_exit(ECANCELED, "%s: No valid checksum found in file", f->fname); } } diff --git a/util/libreboot-utils/lib/command.c b/util/libreboot-utils/lib/command.c index c7048a23..d0f783dd 100644 --- a/util/libreboot-utils/lib/command.c +++ b/util/libreboot-utils/lib/command.c @@ -46,27 +46,27 @@ sanitize_command_index(size_t c) check_command_num(c); if (cmd->argc < 3) - b0rk(EINVAL, "cmd index %lu: argc below 3, %d", + err_exit(EINVAL, "cmd index %lu: argc below 3, %d", (size_t)c, cmd->argc); if (cmd->str == NULL) - b0rk(EINVAL, "cmd index %lu: NULL str", + err_exit(EINVAL, "cmd index %lu: NULL str", (size_t)c); if (*cmd->str == '\0') - b0rk(EINVAL, "cmd index %lu: empty str", + err_exit(EINVAL, "cmd index %lu: empty str", (size_t)c); if (slen(cmd->str, MAX_CMD_LEN +1, &rval) < 0) - b0rk(errno, "Could not get command length"); + err_exit(errno, "Could not get command length"); if (rval > MAX_CMD_LEN) { - b0rk(EINVAL, "cmd index %lu: str too long: %s", + err_exit(EINVAL, "cmd index %lu: str too long: %s", (size_t)c, cmd->str); } if (cmd->run == NULL) - b0rk(EINVAL, "cmd index %lu: cmd ptr null", + err_exit(EINVAL, "cmd index %lu: cmd ptr null", (size_t)c); check_bin(cmd->arg_part, "cmd.arg_part"); @@ -80,19 +80,19 @@ sanitize_command_index(size_t c) case NVM_SIZE: break; default: - b0rk(EINVAL, "Unsupported rw_size: %lu", + err_exit(EINVAL, "Unsupported rw_size: %lu", (size_t)gbe_rw_size); } if (gbe_rw_size > GBE_PART_SIZE) - b0rk(EINVAL, "rw_size larger than GbE part: %lu", + err_exit(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) - b0rk(EINVAL, "invalid cmd.flags setting"); + err_exit(EINVAL, "invalid cmd.flags setting"); } void @@ -110,7 +110,7 @@ set_cmd(int argc, char *argv[]) cmd = x->cmd[c].str; if (scmp(argv[2], cmd, MAX_CMD_LEN, &rval) < 0) - err_no_cleanup(0, EINVAL, + err_exit(EINVAL, "could not compare command strings"); if (rval != 0) continue; /* not the right command */ @@ -123,7 +123,7 @@ set_cmd(int argc, char *argv[]) return; } - err_no_cleanup(0, EINVAL, + err_exit(EINVAL, "Too few args on command '%s'", cmd); } @@ -148,11 +148,11 @@ set_cmd_args(int argc, char *argv[]) /* Maintainer bug */ if (cmd->arg_part && argc < 4) - b0rk(EINVAL, + err_exit(EINVAL, "arg_part set for command that needs argc4"); if (cmd->arg_part && i == CMD_SETMAC) - b0rk(EINVAL, + err_exit(EINVAL, "arg_part set on CMD_SETMAC"); if (i == CMD_SETMAC) { @@ -174,13 +174,13 @@ conv_argv_part_num(const char *part_str) unsigned char ch; if (part_str[0] == '\0' || part_str[1] != '\0') - b0rk(EINVAL, "Partnum string '%s' wrong length", part_str); + err_exit(EINVAL, "Partnum string '%s' wrong length", part_str); /* char signedness is implementation-defined */ ch = (unsigned char)part_str[0]; if (ch < '0' || ch > '1') - b0rk(EINVAL, "Bad part number (%c)", ch); + err_exit(EINVAL, "Bad part number (%c)", ch); return (size_t)(ch - '0'); } @@ -189,7 +189,7 @@ void check_command_num(size_t c) { if (!valid_command(c)) - b0rk(EINVAL, "Invalid run_cmd arg: %lu", + err_exit(EINVAL, "Invalid run_cmd arg: %lu", (size_t)c); } @@ -205,7 +205,7 @@ valid_command(size_t c) cmd = &x->cmd[c]; if (c != cmd->chk) - b0rk(EINVAL, + err_exit(EINVAL, "Invalid cmd chk value (%lu) vs arg: %lu", cmd->chk, c); @@ -240,10 +240,10 @@ parse_mac_string(void) size_t rval; if (slen(x->mac.str, 18, &rval) < 0) - b0rk(EINVAL, "Could not determine MAC length"); + err_exit(EINVAL, "Could not determine MAC length"); if (rval != 17) - b0rk(EINVAL, "MAC address is the wrong length"); + err_exit(EINVAL, "MAC address is the wrong length"); memset(mac->mac_buf, 0, sizeof(mac->mac_buf)); @@ -251,10 +251,10 @@ parse_mac_string(void) set_mac_byte(mac_byte); if ((mac->mac_buf[0] | mac->mac_buf[1] | mac->mac_buf[2]) == 0) - b0rk(EINVAL, "Must not specify all-zeroes MAC address"); + err_exit(EINVAL, "Must not specify all-zeroes MAC address"); if (mac->mac_buf[0] & 1) - b0rk(EINVAL, "Must not specify multicast MAC address"); + err_exit(EINVAL, "Must not specify multicast MAC address"); } void @@ -272,7 +272,7 @@ set_mac_byte(size_t mac_byte_pos) if (mac_str_pos < 15) { if ((separator = mac->str[mac_str_pos + 2]) != ':') - b0rk(EINVAL, "Invalid MAC address separator '%c'", + err_exit(EINVAL, "Invalid MAC address separator '%c'", separator); } @@ -294,9 +294,9 @@ set_mac_nib(size_t mac_str_pos, if ((hex_num = hextonum(mac_ch)) > 15) { if (hex_num >= 17) - b0rk(EIO, "Randomisation failure"); + err_exit(EIO, "Randomisation failure"); else - b0rk(EINVAL, "Invalid character '%c'", + err_exit(EINVAL, "Invalid character '%c'", mac->str[mac_str_pos + mac_nib_pos]); } @@ -509,7 +509,7 @@ cat(size_t nff) if ((size_t)x->cat != nff) { - b0rk(ECANCELED, "erroneous call to cat"); + err_exit(ECANCELED, "erroneous call to cat"); } fflush(NULL); @@ -532,12 +532,12 @@ void cat_buf(unsigned char *b) { if (b == NULL) - b0rk(errno, "null pointer in cat command"); + err_exit(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) - b0rk(errno, "stdout: cat"); + err_exit(errno, "stdout: cat"); } void check_cmd(void (*fn)(void), @@ -547,7 +547,7 @@ check_cmd(void (*fn)(void), size_t i = x->i; if (x->cmd[i].run != fn) - b0rk(ECANCELED, "Running %s, but cmd %s is set", + err_exit(ECANCELED, "Running %s, but cmd %s is set", name, x->cmd[i].str); /* prevent second command @@ -559,6 +559,6 @@ check_cmd(void (*fn)(void), void cmd_helper_err(void) { - b0rk(ECANCELED, + err_exit(ECANCELED, "Erroneously running command twice"); } diff --git a/util/libreboot-utils/lib/file.c b/util/libreboot-utils/lib/file.c index 3ca50889..3620f425 100644 --- a/util/libreboot-utils/lib/file.c +++ b/util/libreboot-utils/lib/file.c @@ -62,27 +62,7 @@ same_file(int fd, struct stat *st_old, return 0; err_same_file: - - if (errno == saved_errno) - errno = ESTALE; - - return -1; -} - -void -xopen(int *fd_ptr, const char *path, int flags, struct stat *st) -{ - if ((*fd_ptr = open(path, flags)) < 0) - err_no_cleanup(0, errno, "%s", path); - - if (fstat(*fd_ptr, st) < 0) - err_no_cleanup(0, errno, "%s: stat", path); - - if (!S_ISREG(st->st_mode)) - err_no_cleanup(0, errno, "%s: not a regular file", path); - - if (lseek_on_eintr(*fd_ptr, 0, SEEK_CUR, 1, 1) == (off_t)-1) - err_no_cleanup(0, errno, "%s: file not seekable", path); + return set_errno(saved_errno, ESTALE); } int @@ -111,12 +91,11 @@ fsync_dir(const char *path) if (if_err(path == NULL, EFAULT) || if_err_sys(slen(path, maxlen, &pathlen) < 0) || if_err(pathlen >= maxlen || pathlen < 0, EMSGSIZE) || - if_err(pathlen == 0, EINVAL) - || - if_err_sys((dirbuf = malloc(pathlen + 1)) == NULL)) + if_err(pathlen == 0, EINVAL)) goto err_fsync_dir; - memcpy(dirbuf, path, pathlen + 1); + memcpy(smalloc(&dirbuf, pathlen + 1), + path, pathlen + 1); slash = strrchr(dirbuf, '/'); if (slash != NULL) { @@ -147,11 +126,7 @@ fsync_dir(const char *path) if_err_sys(fsync_on_eintr(dirfd) == -1)) goto err_fsync_dir; - if (close_on_eintr(dirfd) == -1) { - - dirfd = -1; - goto err_fsync_dir; - } + close_on_eintr(&dirfd); free_and_set_null(&dirbuf); @@ -160,13 +135,11 @@ fsync_dir(const char *path) err_fsync_dir: - if (errno == saved_errno) - errno = EIO; free_and_set_null(&dirbuf); - close_no_err(&dirfd); + close_on_eintr(&dirfd); - return -1; + return set_errno(saved_errno, EIO); } /* rw_file_exact() - Read perfectly or die @@ -269,10 +242,7 @@ rw_file_exact(int fd, unsigned char *mem, size_t nrw, err_rw_file_exact: - if (errno == saved_errno) - errno = EIO; - - return -1; + return set_errno(saved_errno, EIO); } /* prw() - portable read-write with more @@ -451,11 +421,7 @@ real_pread_pwrite: #endif err_prw: - - if (errno == saved_errno) - errno = EIO; - - return -1; + return set_errno(saved_errno, EIO); } int @@ -477,10 +443,7 @@ io_args(int fd, void *mem, size_t nrw, return 0; err_io_args: - if (errno == saved_errno) - errno = EINVAL; - - return -1; + return set_errno(saved_errno, EINVAL); } int @@ -498,10 +461,7 @@ check_file(int fd, struct stat *st) return 0; err_is_file: - if (errno == saved_errno) - errno = EINVAL; - - return -1; + return set_errno(saved_errno, EINVAL); } /* POSIX can say whatever it wants. @@ -523,10 +483,7 @@ rw_over_nrw(ssize_t r, size_t nrw) return r; err_rw_over_nrw: - if (errno == saved_errno) - errno = EIO; - - return -1; + return set_errno(saved_errno, EIO); } off_t @@ -583,73 +540,83 @@ try_err(int loop_err, int errval) void free_and_set_null(char **buf) { - if (buf == NULL || *buf == NULL) + if (buf == NULL) + err_exit(EFAULT, + "null ptr (to ptr for freeing) in free_and_set_null"); + + if (*buf == NULL) return; free(*buf); *buf = NULL; } -/* also returns error code */ -int -close_warn(int *fd, char *s) +void +open_on_eintr(const char *path, + int *fd, int flags, mode_t mode, + struct stat *st) { + int r = -1; int saved_errno = errno; - if (fd == NULL) { - if (s != NULL) - fprintf(stderr, "FAIL: %s: bad fd ptr\n", s); - return -1; - } + if (path == NULL) + err_exit(EINVAL, "open_on_eintr: null path"); - if (*fd < 0 && s != NULL) { - fprintf(stderr, "WARN: %s: already closed\n", s); - } else if (close(*fd) < 0) { - if (s != NULL) - fprintf(stderr, "FAIL: %s: close\n", s); - return -1; - } + if (fd == NULL) + err_exit(EFAULT, "%s: open_on_eintr: null fd ptr", path); - *fd = -1; - errno = saved_errno; + if (*fd >= 0) + err_exit(EBADF, "%s: open_on_eintr: file already open", path); - return 0; -} + do { + r = open(path, flags, mode); + } while (r == -1 && ( + errno == EINTR || errno == EAGAIN || + errno == EWOULDBLOCK || errno == ETXTBSY)); -/* TODO: remove this, and just check - * err on every close. */ -void -close_no_err(int *fd) -{ - int saved_errno = errno; + if (r < 0) + err_exit(errno, "%s: open_on_eintr: could not close", path); - if (fd == NULL || *fd < 0) - return; + *fd = r; - (void) close_on_eintr(*fd); - *fd = -1; + if (st != NULL) { + if (fstat(*fd, st) < 0) + err_exit(errno, "%s: stat", path); + + if (!S_ISREG(st->st_mode)) + err_exit(errno, "%s: not a regular file", path); + } + + if (lseek_on_eintr(*fd, 0, SEEK_CUR, 1, 1) == (off_t)-1) + err_exit(errno, "%s: file not seekable", path); errno = saved_errno; } -/* TODO: make fd a pointer insttead - and automatically reset -1 here */ -int -close_on_eintr(int fd) +void +close_on_eintr(int *fd) { int r; int saved_errno = errno; + if (fd == NULL) + err_exit(EINVAL, "close_on_eintr: null pointer"); + + if (*fd < 0) + return; + do { - r = close(fd); + r = close(*fd); } while (r == -1 && ( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK || errno == ETXTBSY)); - if (r >= 0) - errno = saved_errno; + if (r < 0) + err_exit(errno, "close_on_eintr: could not close"); - return r; + *fd = -1; + + errno = saved_errno; } int @@ -709,8 +676,10 @@ rootfs(void) if (!fs_initialised) { - global_fs.rootfd = - open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC); + global_fs.rootfd = -1; + + open_on_eintr("/", &global_fs.rootfd, + O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0400, NULL); if (global_fs.rootfd < 0) return NULL; @@ -722,6 +691,10 @@ rootfs(void) } /* filesystem sandboxing in userspace + * TODO: + missing length bound check. + potential CPU DoS on very long paths, spammed repeatedly. + perhaps cap at PATH_LEN? */ int fs_resolve_at(int dirfd, const char *path, int flags) @@ -762,7 +735,7 @@ fs_resolve_at(int dirfd, const char *path, int flags) /* close previous fd if not the original input */ if (curfd != dirfd) - (void) close_on_eintr(curfd); + close_on_eintr(&curfd); curfd = nextfd; nextfd = -1; @@ -775,16 +748,25 @@ err: saved_errno = errno; if (nextfd >= 0) - (void) close_on_eintr(nextfd); + close_on_eintr(&nextfd); /* close curfd only if it's not the original */ if (curfd != dirfd && curfd >= 0) - (void) close_on_eintr(curfd); + close_on_eintr(&curfd); errno = saved_errno; return -1; } +/* NOTE: + rejects . and .. but not empty strings + after normalisation. edge case: + ////// + + normalised implicitly, but might be good + to add a defensive check regardless. code + probably not exploitable in current state. + */ int fs_next_component(const char **p, char *name, size_t namesz) @@ -848,7 +830,7 @@ fs_open_component(int dirfd, const char *name, if (!S_ISDIR(st.st_mode)) { - (void) close_on_eintr(fd); + close_on_eintr(&fd); errno = ENOTDIR; return -1; } @@ -862,7 +844,7 @@ fs_dirname_basename(const char *path, char **dir, char **base, int allow_relative) { - char *buf; + char *buf = NULL; char *slash; size_t len; int rval; @@ -874,11 +856,10 @@ fs_dirname_basename(const char *path, #endif if (path == NULL || dir == NULL || base == NULL || - if_err_sys(slen(path, maxlen, &len) < 0) || - if_err_sys((buf = malloc(len + 1)) == NULL)) + if_err_sys(slen(path, maxlen, &len) < 0)) return -1; - memcpy(buf, path, len + 1); + memcpy(smalloc(&buf, len + 1), path, len + 1); /* strip trailing slashes */ while (len > 1 && buf[len - 1] == '/') diff --git a/util/libreboot-utils/lib/io.c b/util/libreboot-utils/lib/io.c index 1f2064a0..eac6073e 100644 --- a/util/libreboot-utils/lib/io.c +++ b/util/libreboot-utils/lib/io.c @@ -4,6 +4,10 @@ * I/O functions specific to nvmutil. */ +/* TODO: local tmpfiles not being deleted + when flags==O_RDONLY e.g. dump command + */ + #include <sys/types.h> #include <sys/stat.h> @@ -27,21 +31,24 @@ open_gbe_file(void) int _flags; - xopen(&f->gbe_fd, f->fname, - cmd->flags | O_BINARY | - O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, &f->gbe_st); + f->gbe_fd = -1; + + open_on_eintr(f->fname, &f->gbe_fd, + O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, + ((cmd->flags & O_ACCMODE) == O_RDONLY) ? 0400 : 0600, + &f->gbe_st); if (f->gbe_st.st_nlink > 1) - b0rk(EINVAL, + err_exit(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) - b0rk(EIO, "%s: file unlinked while open", f->fname); + err_exit(EIO, "%s: file unlinked while open", f->fname); _flags = fcntl(f->gbe_fd, F_GETFL); if (_flags == -1) - b0rk(errno, "%s: fcntl(F_GETFL)", f->fname); + err_exit(errno, "%s: fcntl(F_GETFL)", f->fname); /* O_APPEND allows POSIX write() to ignore * the current write offset and write at EOF, @@ -49,7 +56,7 @@ open_gbe_file(void) */ if (_flags & O_APPEND) - b0rk(EIO, "%s: O_APPEND flag", f->fname); + err_exit(EIO, "%s: O_APPEND flag", f->fname); f->gbe_file_size = f->gbe_st.st_size; @@ -59,11 +66,14 @@ open_gbe_file(void) case SIZE_128KB: break; default: - b0rk(EINVAL, "File size must be 8KB, 16KB or 128KB"); + err_exit(EINVAL, "File size must be 8KB, 16KB or 128KB"); } +/* currently fails (EBADF), locks are advisory anyway: */ +/* if (lock_file(f->gbe_fd, cmd->flags) == -1) - b0rk(errno, "%s: can't lock", f->fname); + err_exit(errno, "%s: can't lock", f->fname); +*/ } void @@ -98,7 +108,7 @@ read_file(void) MAX_ZERO_RW_RETRY, OFF_ERR); if (_r < 0) - b0rk(errno, "%s: read failed", f->fname); + err_exit(errno, "%s: read failed", f->fname); /* copy to tmpfile */ @@ -107,34 +117,34 @@ read_file(void) MAX_ZERO_RW_RETRY, OFF_ERR); if (_r < 0) - b0rk(errno, "%s: %s: copy failed", + err_exit(errno, "%s: %s: copy failed", f->fname, f->tname); /* file size comparison */ if (fstat(f->tmp_fd, &_st) == -1) - b0rk(errno, "%s: stat", f->tname); + err_exit(errno, "%s: stat", f->tname); f->gbe_tmp_size = _st.st_size; if (f->gbe_tmp_size != f->gbe_file_size) - b0rk(EIO, "%s: %s: not the same size", + err_exit(EIO, "%s: %s: not the same size", f->fname, f->tname); /* needs sync, for verification */ if (fsync_on_eintr(f->tmp_fd) == -1) - b0rk(errno, "%s: fsync (tmpfile copy)", f->tname); + err_exit(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) - b0rk(errno, "%s: read failed (cmp)", f->tname); + err_exit(errno, "%s: read failed (cmp)", f->tname); if (memcmp(f->buf, f->bufcmp, f->gbe_file_size) != 0) - b0rk(errno, "%s: %s: read contents differ (pre-test)", + err_exit(errno, "%s: %s: read contents differ (pre-test)", f->fname, f->tname); } @@ -152,10 +162,10 @@ write_gbe_file(void) return; if (same_file(f->tmp_fd, &f->tmp_st, 0) < 0) - b0rk(errno, "%s: file inode/device changed", f->tname); + err_exit(errno, "%s: file inode/device changed", f->tname); if (same_file(f->gbe_fd, &f->gbe_st, 1) < 0) - b0rk(errno, "%s: file has changed", f->fname); + err_exit(errno, "%s: file has changed", f->fname); update_checksum = cmd->chksum_write; @@ -188,7 +198,7 @@ rw_gbe_file_part(size_t p, int rw_type, gbe_rw_size = cmd->rw_size; if (rw_type < IO_PREAD || rw_type > IO_PWRITE) - b0rk(errno, "%s: %s: part %lu: invalid rw_type, %d", + err_exit(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); @@ -198,11 +208,11 @@ rw_gbe_file_part(size_t p, int rw_type, gbe_rw_size, file_offset, rw_type); if (rval == -1) - b0rk(errno, "%s: %s: part %lu", + err_exit(errno, "%s: %s: part %lu", f->fname, rw_type_str, (size_t)p); if ((size_t)rval != gbe_rw_size) - b0rk(EIO, "%s: partial %s: part %lu", + err_exit(EIO, "%s: partial %s: part %lu", f->fname, rw_type_str, (size_t)p); } @@ -226,7 +236,7 @@ write_to_gbe_bin(void) */ if (fsync_on_eintr(f->tmp_fd) == -1) - b0rk(errno, "%s: fsync (pre-verification)", + err_exit(errno, "%s: fsync (pre-verification)", f->tname); check_written_part(0); @@ -235,12 +245,12 @@ write_to_gbe_bin(void) report_io_err_rw(); if (f->io_err_gbe) - b0rk(EIO, "%s: bad write", f->fname); + err_exit(EIO, "%s: bad write", f->fname); saved_errno = errno; - f->io_err_gbe_bin |= -close_warn(&f->tmp_fd, f->tname); - f->io_err_gbe_bin |= -close_warn(&f->gbe_fd, f->fname); + close_on_eintr(&f->tmp_fd); + close_on_eintr(&f->gbe_fd); errno = saved_errno; @@ -307,10 +317,10 @@ check_written_part(size_t p) memset(f->pad, 0xff, sizeof(f->pad)); if (same_file(f->tmp_fd, &f->tmp_st, 0) < 0) - b0rk(errno, "%s: file inode/device changed", f->tname); + err_exit(errno, "%s: file inode/device changed", f->tname); if (same_file(f->gbe_fd, &f->gbe_st, 1) < 0) - b0rk(errno, "%s: file changed during write", f->fname); + err_exit(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); @@ -439,17 +449,8 @@ gbe_mv(void) tmp_gbe_bin_exists = 0; 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; + close_on_eintr(&f->gbe_fd); if (fsync_dir(f->fname) < 0) { f->io_err_gbe_bin = 1; @@ -457,13 +458,7 @@ ret_gbe_mv: } } - if (f->tmp_fd > -1) { - if (close_on_eintr(f->tmp_fd) < 0) { - f->tmp_fd = -1; - rval = -1; - } - f->tmp_fd = -1; - } + close_on_eintr(&f->tmp_fd); /* before this function is called, * tmp_fd may have been moved @@ -475,17 +470,12 @@ ret_gbe_mv: 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; - } + if (rval >= 0) + goto out; + return set_errno(saved_errno, EIO); +out: + errno = saved_errno; return rval; } @@ -540,11 +530,11 @@ gbe_x_offset(size_t p, const char *f_op, const char *d_type, off = ((off_t)p) * (off_t)nsize; if (off > ncmp - GBE_PART_SIZE) - b0rk(ECANCELED, "%s: GbE %s %s out of bounds", + err_exit(ECANCELED, "%s: GbE %s %s out of bounds", f->fname, d_type, f_op); if (off != 0 && off != ncmp >> 1) - b0rk(ECANCELED, "%s: GbE %s %s at bad offset", + err_exit(ECANCELED, "%s: GbE %s %s at bad offset", f->fname, d_type, f_op); return off; diff --git a/util/libreboot-utils/lib/mkhtemp.c b/util/libreboot-utils/lib/mkhtemp.c index c913ce6c..61479b25 100644 --- a/util/libreboot-utils/lib/mkhtemp.c +++ b/util/libreboot-utils/lib/mkhtemp.c @@ -144,13 +144,7 @@ new_tmp_common(int *fd, char **path, int type, goto err; } - dest = malloc(destlen + 1); - if (dest == NULL) { - errno = ENOMEM; - goto err; - } - - memcpy(dest, tmpdir, dirlen); + memcpy(smalloc(&dest, destlen + 1), tmpdir, dirlen); *(dest + dirlen) = '/'; memcpy(dest + dirlen + 1, templatestr, templatestr_len); *(dest + destlen) = '\0'; @@ -170,7 +164,7 @@ new_tmp_common(int *fd, char **path, int type, if (*fd < 0) goto err; - close_no_err(&dirfd); + close_on_eintr(&dirfd); errno = saved_errno; *path = dest; @@ -186,8 +180,8 @@ err: free_and_set_null(&dest); - close_no_err(&dirfd); - close_no_err(fd); + close_on_eintr(&dirfd); + close_on_eintr(fd); /* where a TMPDIR isn't found, and we err, * we pass this back through for the @@ -212,8 +206,8 @@ env_tmpdir(int bypass_all_sticky_checks, char **tmpdir, int allow_noworld_unsticky; int saved_errno = errno; - char tmp[] = "/tmp"; - char vartmp[] = "/var/tmp"; + static const char tmp[] = "/tmp"; + static const char vartmp[] = "/var/tmp"; /* tmpdir is a user override, if set */ if (override_tmpdir == NULL) @@ -244,26 +238,26 @@ env_tmpdir(int bypass_all_sticky_checks, char **tmpdir, allow_noworld_unsticky = 0; - if (world_writeable_and_sticky("/tmp", + if (world_writeable_and_sticky(tmp, allow_noworld_unsticky, bypass_all_sticky_checks)) { if (tmpdir != NULL) - *tmpdir = tmp; + *tmpdir = (char *)tmp; errno = saved_errno; - return "/tmp"; + return (char *)tmp; } - if (world_writeable_and_sticky("/var/tmp", + if (world_writeable_and_sticky(vartmp, allow_noworld_unsticky, bypass_all_sticky_checks)) { if (tmpdir != NULL) - *tmpdir = vartmp; + *tmpdir = (char *)vartmp; errno = saved_errno; - return "/var/tmp"; + return (char *)vartmp; } return NULL; @@ -301,11 +295,7 @@ tmpdir_policy(const char *path, return 0; err_tmpdir_policy: - - if (errno == saved_errno) - errno = EIO; - - return -1; + return set_errno(saved_errno, EIO); } int @@ -337,11 +327,11 @@ same_dir(const char *a, const char *b) if (rval_scmp == 0) goto success_same_dir; - fd_a = fs_open(a, O_RDONLY | O_DIRECTORY); + fd_a = fs_open(a, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); if (fd_a < 0) goto err_same_dir; - fd_b = fs_open(b, O_RDONLY | O_DIRECTORY); + fd_b = fs_open(b, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); if (fd_b < 0) goto err_same_dir; @@ -354,8 +344,8 @@ same_dir(const char *a, const char *b) if (st_a.st_dev == st_b.st_dev && st_a.st_ino == st_b.st_ino) { - close_no_err(&fd_a); - close_no_err(&fd_b); + close_on_eintr(&fd_a); + close_on_eintr(&fd_b); success_same_dir: @@ -366,8 +356,8 @@ success_same_dir: return 1; } - close_no_err(&fd_a); - close_no_err(&fd_b); + close_on_eintr(&fd_a); + close_on_eintr(&fd_b); /* FAILURE (logical) */ @@ -380,13 +370,10 @@ err_same_dir: /* FAILURE (probably syscall) */ - close_no_err(&fd_a); - close_no_err(&fd_b); + close_on_eintr(&fd_a); + close_on_eintr(&fd_b); - if (errno == saved_errno) - errno = EIO; - - return -1; + return set_errno(saved_errno, EIO); } /* bypass_all_sticky_checks: if set, @@ -473,20 +460,15 @@ world_writeable_and_sticky( goto sticky_hell; /* heaven visa denied */ sticky_heaven: - - close_no_err(&dirfd); + close_on_eintr(&dirfd); errno = saved_errno; return 1; sticky_hell: + close_on_eintr(&dirfd); - if (errno == saved_errno) - errno = EPERM; - - close_no_err(&dirfd); - errno = saved_errno; - + (void) set_errno(saved_errno, EPERM); return 0; } @@ -531,6 +513,17 @@ sticky_hell: * file ownership under hardened mode * (only lets you touch your own files/dirs) */ +/* + TODO: + some variables e.g. template vs suffix, + assumes they match. + we should test this explicitly, + but the way this is called is + currently safe - this would however + be nice for future library use + by outside projects. + this whole code needs to be reorganised +*/ int mkhtemp(int *fd, struct stat *st, @@ -585,11 +578,8 @@ mkhtemp(int *fd, fname_len) != 0, EINVAL)) return -1; - if((fname_copy = malloc(fname_len + 1)) == NULL) - goto err; - /* fname_copy = templatestr region only; p points to trailing XXXXXX */ - memcpy(fname_copy, + memcpy(smalloc(&fname_copy, fname_len + 1), template + len - fname_len, fname_len + 1); p = fname_copy + fname_len - xc; @@ -616,7 +606,7 @@ mkhtemp(int *fd, errno = EEXIST; err: - close_no_err(fd); + close_on_eintr(fd); success: free_and_set_null(&fname_copy); @@ -742,7 +732,7 @@ mkhtemp_try_create(int dirfd, goto out; err: - close_no_err(fd); + close_on_eintr(fd); if (file_created) (void) unlinkat(dirfd, fname_copy, 0); @@ -837,11 +827,17 @@ err: if (linked) (void) unlinkat(dirfd, fname_copy, 0); - close_no_err(&tmpfd); + close_on_eintr(&tmpfd); return -1; } #endif +/* TODO: potential infinite loop under entropy failure. + * e.g. keeps returning low quality RNG, or atacker + * has control (DoS attack potential). + * possible solution: add a timeout (and abort if + * the timeout is reached) + */ int mkhtemp_fill_random(char *p, size_t xc) { @@ -921,6 +917,8 @@ int secure_file(int *fd, if (lock_file(*fd, flags) == -1) goto err_demons; + /* TODO: why would this be NULL? audit + * to find out. we should always verify! */ if (expected != NULL) if (fd_verify_identity(*fd, expected, &st_now) < 0) goto err_demons; @@ -933,11 +931,7 @@ int secure_file(int *fd, return 0; err_demons: - - if (errno == saved_errno) - errno = EIO; - - return -1; + return set_errno(saved_errno, EIO); } int @@ -1047,9 +1041,5 @@ lock_file(int fd, int flags) return 0; err_lock_file: - - if (errno == saved_errno) - errno = EIO; - - return -1; + return set_errno(saved_errno, EIO); } diff --git a/util/libreboot-utils/lib/num.c b/util/libreboot-utils/lib/num.c index 79d6b409..66fc26f1 100644 --- a/util/libreboot-utils/lib/num.c +++ b/util/libreboot-utils/lib/num.c @@ -33,50 +33,24 @@ hextonum(char ch_s) ch = (unsigned char)ch_s; - if ((unsigned int)(ch - '0') <= 9) { - - rval = ch - '0'; - goto hextonum_success; - } + if ((unsigned int)(ch - '0') <= 9) + return ch - '0'; ch |= 0x20; - if ((unsigned int)(ch - 'a') <= 5) { - - rval = ch - 'a' + 10; - goto hextonum_success; - } - - if (ch == '?' || ch == 'x') { - - rset(&rval, sizeof(rval)); - - 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 */ + if ((unsigned int)(ch - 'a') <= 5) + return ch - 'a' + 10; - return 16; /* invalid character */ + if (ch == '?' || ch == 'x') + return rsize(16); /* <-- with rejection sampling! */ - /* caller just checks >15. */ + return 16; } void check_bin(size_t a, const char *a_name) { if (a > 1) - err_no_cleanup(0, EINVAL, "%s must be 0 or 1, but is %lu", + err_exit(EINVAL, "%s must be 0 or 1, but is %lu", a_name, (size_t)a); } diff --git a/util/libreboot-utils/lib/rand.c b/util/libreboot-utils/lib/rand.c index 3155eec3..9edc8a5b 100644 --- a/util/libreboot-utils/lib/rand.c +++ b/util/libreboot-utils/lib/rand.c @@ -72,17 +72,14 @@ * or your program dies. */ -void * -rmalloc(size_t *rval) -{ - return if_err(rval == NULL, EFAULT) ? - NULL : mkrstr(*rval = rsize(BUFSIZ)); -} - size_t rsize(size_t n) { size_t rval = SIZE_MAX; + if (!n) + err_exit(EFAULT, "rsize: division by zero"); + + /* rejection sampling (clamp rand to eliminate modulo bias) */ for (; rval >= SIZE_MAX - (SIZE_MAX % n); rset(&rval, sizeof(rval))); return rval % n; @@ -95,13 +92,13 @@ mkrstr(size_t n) /* emulates spkmodem-decode */ size_t i; if (n == 0) - err_no_cleanup(0, EPERM, "mkrbuf: zero-byte request"); + err_exit(EPERM, "mkrbuf: zero-byte request"); if (n >= SIZE_MAX - 1) - err_no_cleanup(0, EOVERFLOW, "mkrbuf: overflow"); + err_exit(EOVERFLOW, "mkrbuf: overflow"); if (if_err((s = mkrbuf(n + 1)) == NULL, EFAULT)) - err_no_cleanup(0, EFAULT, "mkrstr: null"); + err_exit(EFAULT, "mkrstr: null"); for (i = 0; i < n; i++) while(*(s + i) == '\0') @@ -115,18 +112,8 @@ mkrstr(size_t n) /* emulates spkmodem-decode */ void * mkrbuf(size_t n) { - void *buf = ""; - - if (n == 0) - err_no_cleanup(0, EPERM, "mkrbuf: zero-byte request"); - - if (n >= SIZE_MAX - 1) - err_no_cleanup(0, EOVERFLOW, "integer overflow in mkrbuf"); - - if ((buf = malloc(n)) == NULL) - err_no_cleanup(0, ENOMEM, "mkrbuf: malloc"); - - rset(buf, n); + void *buf = NULL; + rset(vmalloc(&buf, n), n); return buf; /* basically malloc() but with rand */ } @@ -139,7 +126,7 @@ rset(void *buf, size_t n) goto err; if (n == 0) - err_no_cleanup(0, EPERM, "rset: zero-byte request"); + err_exit(EPERM, "rset: zero-byte request"); #if (defined(__OpenBSD__) || defined(__FreeBSD__) || \ defined(__NetBSD__) || defined(__APPLE__) || \ @@ -155,9 +142,7 @@ rset(void *buf, size_t n) #if defined(USE_URANDOM) && \ ((USE_URANDOM) > 0) int fd = -1; - - if ((fd = open("/dev/urandom", O_RDONLY)) < 0) - goto err; + open_on_eintr("/dev/urandom", &fd, O_RDONLY, 0400, NULL); retry_rand: if ((rc = read(fd, (unsigned char *)buf + off, n - off)) < 0) { #elif defined(__linux__) @@ -182,7 +167,7 @@ retry_rand: #if defined(USE_URANDOM) && \ ((USE_URANDOM) > 0) - close_no_err(&fd); + close_on_eintr(&fd); #endif goto out; #endif @@ -192,9 +177,9 @@ out: err: #if defined(USE_URANDOM) && \ ((USE_URANDOM) > 0) - close_no_err(&fd); + close_on_eintr(&fd); #endif - err_no_cleanup(0, ECANCELED, + err_exit(ECANCELED, "Randomisation failure, possibly unsupported in your kernel"); exit(EXIT_FAILURE); } diff --git a/util/libreboot-utils/lib/state.c b/util/libreboot-utils/lib/state.c index 41c851fb..a3cd5b1f 100644 --- a/util/libreboot-utils/lib/state.c +++ b/util/libreboot-utils/lib/state.c @@ -4,9 +4,6 @@ * State machine (singleton) for nvmutil data. */ -#ifdef __OpenBSD__ -#include <sys/param.h> -#endif #include <sys/types.h> #include <sys/stat.h> @@ -98,9 +95,9 @@ xstart(int argc, char *argv[]) return &us; if (argc < 3) - err_no_cleanup(0, EINVAL, "xstart: Too few arguments"); + err_exit(EINVAL, "xstart: Too few arguments"); if (argv == NULL) - err_no_cleanup(0, EINVAL, "xstart: NULL argv"); + err_exit(EINVAL, "xstart: NULL argv"); first_run = 0; @@ -113,41 +110,41 @@ xstart(int argc, char *argv[]) us.f.tname = NULL; if ((realdir = realpath(us.f.fname, NULL)) == NULL) - err_no_cleanup(0, errno, "xstart: can't get realpath of %s", + err_exit(errno, "xstart: can't get realpath of %s", us.f.fname); if (fs_dirname_basename(realdir, &dir, &base, 0) < 0) - err_no_cleanup(0, errno, "xstart: don't know CWD of %s", + err_exit(errno, "xstart: don't know CWD of %s", us.f.fname); if ((us.f.base = strdup(base)) == NULL) - err_no_cleanup(0, errno, "strdup base"); + err_exit(errno, "strdup base"); us.f.dirfd = fs_open(dir, O_RDONLY | O_DIRECTORY); if (us.f.dirfd < 0) - err_no_cleanup(0, errno, "%s: open dir", dir); + err_exit(errno, "%s: open dir", dir); if (new_tmpfile(&us.f.tmp_fd, &us.f.tname, dir, ".gbe.XXXXXXXXXX") < 0) - err_no_cleanup(0, errno, "%s", us.f.tname); + err_exit(errno, "%s", us.f.tname); if (fs_dirname_basename(us.f.tname, &tmpdir, &tmpbase_local, 0) < 0) - err_no_cleanup(0, errno, "tmp basename"); + err_exit(errno, "tmp basename"); us.f.tmpbase = strdup(tmpbase_local); if (us.f.tmpbase == NULL) - err_no_cleanup(0, errno, "strdup tmpbase"); + err_exit(errno, "strdup tmpbase"); free_and_set_null(&tmpdir); if (us.f.tname == NULL) - err_no_cleanup(0, errno, "x->f.tname null"); + err_exit(errno, "x->f.tname null"); if (*us.f.tname == '\0') - err_no_cleanup(0, errno, "x->f.tname empty"); + err_exit(errno, "x->f.tname empty"); if (fstat(us.f.tmp_fd, &us.f.tmp_st) < 0) - err_no_cleanup(0, errno, "%s: stat", us.f.tname); + err_exit(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)); @@ -164,70 +161,7 @@ xstatus(void) struct xstate *x = xstart(0, NULL); if (x == NULL) - err_no_cleanup(0, EACCES, "NULL pointer to xstate"); + err_exit(EACCES, "NULL pointer to xstate"); return x; } - -void -b0rk(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); -} - -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; - - close_no_err(&f->gbe_fd); - close_no_err(&f->tmp_fd); - close_no_err(&f->tmp_fd); - - if (f->tname != NULL) - if (unlink(f->tname) == -1) - close_err = 1; - - close_no_err(&f->dirfd); - free_and_set_null(&f->base); - free_and_set_null(&f->tmpbase); - } - - if (saved_errno) - errno = saved_errno; - - if (close_err) - return -1; - - return 0; -} diff --git a/util/libreboot-utils/lib/string.c b/util/libreboot-utils/lib/string.c index c6e09752..76141c58 100644 --- a/util/libreboot-utils/lib/string.c +++ b/util/libreboot-utils/lib/string.c @@ -19,6 +19,51 @@ #include "../include/common.h" +/* safe(ish) malloc. + + use this and free_and_set_null() + in your program, to reduce the + chance of use after frees! + + if you use these functions in the + intended way, you will greatly reduce + the number of bugs in your code + */ +char * +smalloc(char **buf, size_t size) +{ + return (char *)vmalloc((void **)buf, size); +} +void * +vmalloc(void **buf, size_t size) +{ + void *rval = NULL; + + if (size >= SIZE_MAX - 1) + err_exit(EOVERFLOW, "integer overflow in vmalloc"); + if (buf == NULL) + err_exit(EFAULT, "Bad pointer passed to vmalloc"); + + /* lots of programs will + * re-initialise a buffer + * that was allocated, without + * freeing or NULLing it. this + * is here intentionally, to + * force the programmer to behave + */ + if (*buf != NULL) + err_exit(EFAULT, "Non-null pointer given to vmalloc"); + + if (!size) + err_exit(EFAULT, + "Tried to vmalloc(0) and that is very bad. Fix it now"); + + if ((rval = malloc(size)) == NULL) + err_exit(errno, "malloc fail in vmalloc"); + + return *buf = rval; +} + /* strict strcmp */ int scmp(const char *a, @@ -98,19 +143,16 @@ sdup(const char *s, size_t n, char **dest) { size_t size; - char *rval; + char *rval = NULL; if (dest == NULL || - slen(s, n, &size) < 0 || - if_err(size == SIZE_MAX, EOVERFLOW) || - (rval = malloc(size + 1)) == NULL) { - + slen(s, n, &size) < 0) { if (dest != NULL) *dest = NULL; return -1; } - memcpy(rval, s, size); + memcpy(smalloc(&rval, size + 1), s, size); *(rval + size) = '\0'; *dest = rval; @@ -133,10 +175,11 @@ scatn(ssize_t sc, const char **sv, if (if_err(sc <= 0, EINVAL) || if_err(sc > SIZE_MAX / sizeof(size_t), EOVERFLOW) || - if_err(sv == NULL, EINVAL) || - if_err((size = malloc(sizeof(size_t) * sc)) == NULL, ENOMEM)) + if_err(sv == NULL, EINVAL)) goto err; + vmalloc((void **)&size, sizeof(size_t) * sc); + for (i = 0; i < sc; i++, ts += size[i]) if (if_err(sv[i] == NULL, EINVAL) || slen(sv[i], max, &size[i]) < 0 || @@ -145,10 +188,10 @@ scatn(ssize_t sc, const char **sv, goto err; if (if_err(ts > SIZE_MAX - 1, EOVERFLOW) || - if_err(ts > max - 1, EOVERFLOW) || - if_err((ct = malloc(ts + 1)) == NULL, ENOMEM)) + if_err(ts > max - 1, EOVERFLOW)) goto err; + smalloc(&ct, ts + 1); for (ts = i = 0; i < sc; i++, ts += size[i]) memcpy(ct + ts, sv[i], size[i]); @@ -158,14 +201,10 @@ scatn(ssize_t sc, const char **sv, errno = saved_errno; return 0; err: - if (ct != NULL) - free(ct); - if (size != NULL) - free(size); - if (errno == saved_errno) - errno = EFAULT; + free_and_set_null(&ct); + free_and_set_null((char **)&size); - return -1; + return set_errno(saved_errno, EFAULT); } /* strict strcat */ @@ -175,20 +214,20 @@ scat(const char *s1, const char *s2, { size_t size1; size_t size2; - char *rval; + char *rval = NULL; if (dest == NULL || slen(s1, n, &size1) < 0 || slen(s2, n, &size2) < 0 || - if_err(size1 > SIZE_MAX - size2 - 1, EOVERFLOW) || - (rval = malloc(size1 + size2 + 1)) == NULL) { + if_err(size1 > SIZE_MAX - size2 - 1, EOVERFLOW)) { if (dest != NULL) *dest = NULL; return -1; } - memcpy(rval, s1, size1); + memcpy(smalloc(&rval, size1 + size2 + 1), + s1, size1); memcpy(rval + size1, s2, size2); *(rval + size1 + size2) = '\0'; @@ -210,17 +249,17 @@ dcat(const char *s, size_t n, if (dest1 == NULL || dest2 == NULL || slen(s, n, &size) < 0 || if_err(size == SIZE_MAX, EOVERFLOW) || - if_err(off >= size, EOVERFLOW) || - (rval1 = malloc(off + 1)) == NULL || - (rval2 = malloc(size - off + 1)) == NULL) { + if_err(off >= size, EOVERFLOW)) { goto err; } - memcpy(rval1, s, off); + memcpy(smalloc(&rval1, off + 1), + s, off); *(rval1 + off) = '\0'; - memcpy(rval2, s + off, size - off); + memcpy(smalloc(&rval2, size - off +1), + s + off, size - off); *(rval2 + size - off) = '\0'; *dest1 = rval1; @@ -242,21 +281,32 @@ err: return -1; } +/* on functions that return with errno, + * i sometimes have a default fallback, + * which is set if errno wasn't changed, + * under error condition. + */ +int +set_errno(int saved_errno, int fallback) +{ + if (errno == saved_errno) + errno = fallback; + return -1; +} + /* the one for nvmutil state is in state.c */ /* this one just exits */ void -err_no_cleanup(int stfu, int nvm_errval, const char *msg, ...) +err_exit(int nvm_errval, const char *msg, ...) { va_list args; int saved_errno = errno; const char *p; -#if defined(__OpenBSD__) && defined(OpenBSD) -#if (OpenBSD) >= 509 - if (pledge("stdio", NULL) == -1) - fprintf(stderr, "pledge failure during exit"); -#endif -#endif + func_t err_cleanup = errhook(NULL); + err_cleanup(); + errno = saved_errno; + if (!errno) saved_errno = errno = ECANCELED; @@ -275,6 +325,37 @@ err_no_cleanup(int stfu, int nvm_errval, const char *msg, ...) exit(EXIT_FAILURE); } +/* the err function will + * call this upon exit, and + * cleanup will be performed + * e.g. you might want to + * close some files, depending + * on your program. + * see: err_exit() + */ +func_t errhook(func_t ptr) +{ + static int set = 0; + static func_t hook = NULL; + + if (!set) { + set = 1; + + if (ptr == NULL) + hook = no_op; + else + hook = ptr; + } + + return hook; +} + +void +no_op(void) +{ + return; +} + const char * getnvmprogname(void) { @@ -310,17 +391,18 @@ lbgetprogname(char *argv0) if (!setname) { if (if_err(argv0 == NULL || *argv0 == '\0', EFAULT) || - slen(argv0, 4096, &len) < 0 || - (progname = malloc(len + 1)) == NULL) + slen(argv0, 4096, &len) < 0) return NULL; - memcpy(progname, argv0, len + 1); + memcpy(smalloc(&progname, len + 1), argv0, len + 1); setname = 1; } return progname; } +/* https://man.openbsd.org/pledge.2 + https://man.openbsd.org/unveil.2 */ int xpledgex(const char *promises, const char *execpromises) { @@ -328,12 +410,11 @@ xpledgex(const char *promises, const char *execpromises) (void) promises, (void) execpromises, (void) saved_errno; #ifdef __OpenBSD__ if (pledge(promises, execpromises) == -1) - err_no_cleanup(0, errno, "pledge"); + err_exit(errno, "pledge"); #endif errno = saved_errno; return 0; } - int xunveilx(const char *path, const char *permissions) { @@ -341,7 +422,7 @@ xunveilx(const char *path, const char *permissions) (void) path, (void) permissions, (void) saved_errno; #ifdef __OpenBSD__ if (pledge(promises, execpromises) == -1) - err_no_cleanup(0, errno, "pledge"); + err_exit(errno, "pledge"); #endif errno = saved_errno; return 0; diff --git a/util/libreboot-utils/lib/usage.c b/util/libreboot-utils/lib/usage.c index 2b5a93ca..ebec119e 100644 --- a/util/libreboot-utils/lib/usage.c +++ b/util/libreboot-utils/lib/usage.c @@ -26,5 +26,5 @@ usage(void) util, util, util, util, util, util, util); - b0rk(EINVAL, "Too few arguments"); + err_exit(EINVAL, "Too few arguments"); } diff --git a/util/libreboot-utils/lib/word.c b/util/libreboot-utils/lib/word.c index 6563e67a..85e1d88b 100644 --- a/util/libreboot-utils/lib/word.c +++ b/util/libreboot-utils/lib/word.c @@ -63,6 +63,6 @@ check_nvm_bound(size_t c, size_t p) check_bin(p, "part number"); if (c >= NVM_WORDS) - b0rk(ECANCELED, "check_nvm_bound: out of bounds %lu", + err_exit(ECANCELED, "check_nvm_bound: out of bounds %lu", (size_t)c); } diff --git a/util/libreboot-utils/lottery.c b/util/libreboot-utils/lottery.c index b8196729..cbe8a871 100644 --- a/util/libreboot-utils/lottery.c +++ b/util/libreboot-utils/lottery.c @@ -1,75 +1,86 @@ /* SPDX-License-Identifier: MIT ( >:3 ) * Copyright (c) 2026 Leah Rowe <leah@libreboot.org> /| |\ - / \ */ + Something something non-determinism / \ */ + +#include <ctype.h> +#include <stddef.h> #include <stdio.h> #include <stdint.h> -#include <stdlib.h> /* (^.>) - are u lucky? */ -#include <string.h> /* \| /= */ -#include "include/common.h" /* l \ */ +#include <string.h> +#include <stdlib.h> +#include "include/common.h" + +static void +exit_cleanup(void); -#define len_hell(x, y) ((((x) - (y)) < BUFSIZ) ? ((x) - (y)) : BUFSIZ) +static void +spew_buf(const void *data, size_t len); -static int rigged(void) +int +main(int argc, char **argv) { - size_t size[5] = { 0, 0, 0, SIZE_MAX, SIZE_MAX}; - char *b1 = NULL, *b2 = NULL; - char *s = NULL; - size_t e = 0; - - int rval = 1; - -for (e = 0; e < SIZE_MAX; e++) { - if (rsize(e | 1) == 666) - goto heaven; - - size[3] = SIZE_MAX; - size[4] = SIZE_MAX; -hell: - if (!size[3]) { - if (!size[4]) - continue; - else --size[4]; - } else --size[3]; - - free_and_set_null(&b1); - free_and_set_null(&b2); - - size[0] = rsize(SIZE_MAX); /* \( ^o^)/ - then come play! */ - size[1] = rsize(SIZE_MAX); /* | | */ -/* / \ */ - if (!(size[0] && (size[0] == size[1]) && (size[0] <= SIZE_MAX)) || - ((size[0] & 1) && (*(b1 = mkrstr(1)) != *(b2 = mkrstr(1))))) - goto out; /* \(^-^)/ - it could be you! */ - if (!(size[0] &= ~(size_t)1)) /* \ / */ - goto hell; /* / \ */ - - if (s == NULL) { - if ((s = malloc(BUFSIZ)) == NULL) - goto out; - } + int same = 0; + char *buf; + + (void) argc, (void) argv; + xpledgex("stdio", NULL); + + (void) errhook(exit_cleanup); + + buf = mkrbuf(BUFSIZ + 1); + if (!memcmp(buf, buf + (BUFSIZ >> 1), BUFSIZ >> 1)) + same = 1; - for (size[1] = 0; size[1] < size[0]; size[1] += BUFSIZ) { - rset(s, size[2] = len_hell(size[0], size[1])); - if (!memcmp(s, s + (size[2] >> 1), size[2] >> 1)) - goto out; - } + if (argc < 2) /* no spew */ + spew_buf(buf, BUFSIZ); + free(buf); - goto hell; + fprintf(stderr, "\n%s\n", same ? "You win!" : "You lose!"); + return same ^ 1; } -heaven: - rval = 0; /* <-- WINNER!!! */ -out: - free_and_set_null(&b1); - free_and_set_null(&b2); - free_and_set_null(&s); - - return rval; + +static void +spew_buf(const void *data, size_t len) +{ + const unsigned char *buf = data; + unsigned char c; + size_t i, j; + + if (buf == NULL || + len == 0) + return; + + for (i = 0; i < len; i += 16) { + + printf("%08zx ", i); + + for (j = 0; j < 16; j++) { + + if (i + j < len) + printf("%02x ", buf[i + j]); + else + printf(" "); + + if (j == 7) + printf(" "); + } + + printf(" |"); + + for (j = 0; j < 16 && i + j < len; j++) { + + c = buf[i + j]; + printf("%c", isprint(c) ? c : '.'); + } + + printf("|\n"); + } + + printf("%08zx\n", len); } -/* \(^o^)/ - come back soon! */ -int main(int argc, char **argv) /* \ / */ -{ /* / \ */ - xpledgex("stdio", NULL); /* */ - printf("%s\n", (argc = rigged()) ? "You lose!" : "You win!"); - return argc; +static void +exit_cleanup(void) +{ + return; } diff --git a/util/libreboot-utils/mkhtemp.c b/util/libreboot-utils/mkhtemp.c index 7564800a..5be5a38a 100644 --- a/util/libreboot-utils/mkhtemp.c +++ b/util/libreboot-utils/mkhtemp.c @@ -19,10 +19,6 @@ #define _GNU_SOURCE 1 #endif -#ifdef __OpenBSD__ -#include <sys/param.h> /* pledge(2) */ -#endif - #include <sys/types.h> #include <sys/stat.h> @@ -38,6 +34,9 @@ #include "include/common.h" +static void +exit_cleanup(void); + int main(int argc, char *argv[]) { @@ -61,18 +60,14 @@ main(int argc, char *argv[]) int fd = -1; int type = MKHTEMP_FILE; - int stfu = 0; /* -q option */ + + (void) errhook(exit_cleanup); if (lbgetprogname(argv[0]) == NULL) - err_no_cleanup(stfu, errno, "could not set progname"); + err_exit(errno, "could not set progname"); -/* https://man.openbsd.org/pledge.2 */ -#if defined(__OpenBSD__) && defined(OpenBSD) -#if (OpenBSD) >= 509 - if (pledge("stdio flock rpath wpath cpath", NULL) == -1) - goto err_usage; -#endif -#endif + /* https://man.openbsd.org/pledge.2 */ + xpledgex("stdio flock rpath wpath cpath", NULL); while ((c = getopt(argc, argv, "qdp:")) != -1) { @@ -88,7 +83,6 @@ main(int argc, char *argv[]) case 'q': /* don't print errors */ /* (exit status unchanged) */ - stfu = 1; break; default: @@ -104,14 +98,14 @@ main(int argc, char *argv[]) /* custom template e.g. foo.XXXXXXXXXXXXXXXXXXXXX */ if (template != NULL) { if (slen(template, maxlen, &tlen) < 0) - err_no_cleanup(stfu, EINVAL, + err_exit(EINVAL, "invalid template"); for (p = template + tlen; p > template && *--p == 'X'; xc++); if (xc < 3) /* the gnu mktemp errs on less than 3 */ - err_no_cleanup(stfu, EINVAL, + err_exit(EINVAL, "template must have 3 X or more on end (12+ advised"); } @@ -125,36 +119,37 @@ main(int argc, char *argv[]) if (tmpdir != NULL) { rp = realpath(tmpdir, resolved); if (rp == NULL) - err_no_cleanup(stfu, errno, "%s", tmpdir); + err_exit(errno, "%s", tmpdir); tmpdir = resolved; } if (new_tmp_common(&fd, &s, type, tmpdir, template) < 0) - err_no_cleanup(stfu, errno, "%s", s); + err_exit(errno, "%s", s); -#if defined(__OpenBSD__) && defined(OpenBSD) -#if (OpenBSD) >= 509 - if (pledge("stdio", NULL) == -1) - err_no_cleanup(stfu, errno, "pledge, exit"); -#endif -#endif + xpledgex("stdio", NULL); if (s == NULL) - err_no_cleanup(stfu, EFAULT, "bad string initialisation"); + err_exit(EFAULT, "bad string initialisation"); if (*s == '\0') - err_no_cleanup(stfu, EFAULT, "empty string initialisation"); + err_exit(EFAULT, "empty string initialisation"); if (slen(s, maxlen, &len) < 0) - err_no_cleanup(stfu, EFAULT, "unterminated string initialisiert"); + err_exit(EFAULT, "unterminated string initialisiert"); printf("%s\n", s); return EXIT_SUCCESS; err_usage: - err_no_cleanup(stfu, EINVAL, + err_exit(EINVAL, "usage: %s [-d] [-p dir] [template]\n", getnvmprogname()); +} + +static void +exit_cleanup(void) +{ + return; }/* ( >:3 ) diff --git a/util/libreboot-utils/nvmutil.c b/util/libreboot-utils/nvmutil.c index 0eed440c..bab1945d 100644 --- a/util/libreboot-utils/nvmutil.c +++ b/util/libreboot-utils/nvmutil.c @@ -6,70 +6,50 @@ * These images configure your Intel Gigabit Ethernet adapter. */ -#ifdef __OpenBSD__ -/* for pledge/unveil test: - */ -#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" +static void +exit_cleanup(void); + int main(int argc, char *argv[]) { struct xstate *x; - struct commands *cmd; struct xfile *f; - size_t c; + (void) errhook(exit_cleanup); + if (lbgetprogname(argv[0]) == NULL) - err_no_cleanup(0, errno, "could not set progname"); - -/* https://man.openbsd.org/pledge.2 - https://man.openbsd.org/unveil.2 */ -#if defined(__OpenBSD__) && defined(OpenBSD) -#if (OpenBSD) >= 604 - if (pledge("stdio flock rpath wpath cpath unveil", NULL) == -1) - err_no_cleanup(0, errno, "pledge plus unveil, main"); -#if defined(USE_URANDOM) && \ - ((USE_URANDOM) > 0) - if (unveil("/dev/null", "r") == -1) - err_no_cleanup(0, errno, "unveil r: /dev/null"); -#else - if (unveil("/dev/urandom", "r") == -1) - err_no_cleanup(0, errno, "unveil r: /dev/urandom"); -#endif -#elif (OpenBSD) >= 509 - if (pledge("stdio flock rpath wpath cpath", NULL) == -1) - err_no_cleanup(0, errno, "pledge, main"); -#endif -#endif + err_exit(errno, "could not set progname"); + + xpledgex("stdio flock rpath wpath cpath unveil", NULL); + xunveilx("/dev/urandom", "r"); #ifndef S_ISREG - err_no_cleanup(0, ECANCELED, + err_exit(ECANCELED, "Can't determine file types (S_ISREG undefined)"); #endif #if ((CHAR_BIT) != 8) - err_no_cleanup(0, ECANCELED, "Unsupported char size"); + err_exit(ECANCELED, "Unsupported char size"); #endif - x = xstart(argc, argv); - - if (x == NULL) - err_no_cleanup(0, ECANCELED, "NULL state on init"); + if ((x = xstart(argc, argv)) == NULL) + err_exit(ECANCELED, "NULL state on init"); /* parse user command */ /* TODO: CHECK ACCESSES VIA xstatus() */ @@ -79,45 +59,22 @@ main(int argc, char *argv[]) cmd = &x->cmd[x->i]; f = &x->f; -/* https://man.openbsd.org/pledge.2 - https://man.openbsd.org/unveil.2 */ -#if defined(__OpenBSD__) && defined(OpenBSD) -#if (OpenBSD) >= 604 - - if ((us.cmd[i].flags & O_ACCMODE) == O_RDONLY) { - if (unveil(us.f.fname, "r") == -1) - b0rk(errno, "%s: unveil r", us.f.fname); - } else { - if (unveil(us.f.fname, "rwc") == -1) - b0rk(errno, "%s: unveil rw", us.f.fname); - } + if ((cmd->flags & O_ACCMODE) == O_RDONLY) + xunveilx(f->fname, "r"); + else + xunveilx(f->fname, "rwc"); - if (unveil(us.f.tname, "rwc") == -1) - b0rk(errno, "unveil rwc: %s", us.f.tname); - - if (unveil(NULL, NULL) == -1) - b0rk(errno, "unveil block (rw)"); - - if (pledge("stdio flock rpath wpath cpath", NULL) == -1) - b0rk(errno, "pledge (kill unveil)"); - -#elif (OpenBSD) >= 509 - if (pledge("stdio flock rpath wpath cpath", NULL) == -1) - b0rk(errno, "pledge"); -#endif -#endif + xunveilx(f->tname, "rwc"); + xunveilx(NULL, NULL); + xpledgex("stdio flock rpath wpath cpath", NULL); if (cmd->run == NULL) - b0rk(errno, "Command not set"); - + err_exit(errno, "Command not set"); sanitize_command_list(); - open_gbe_file(); - copy_gbe(); read_checksums(); - cmd->run(); for (c = 0; c < items(x->cmd); c++) @@ -126,13 +83,17 @@ main(int argc, char *argv[]) if ((cmd->flags & O_ACCMODE) == O_RDWR) write_to_gbe_bin(); - if (exit_cleanup() == -1) - b0rk(EIO, "%s: close", f->fname); - + exit_cleanup(); if (f->io_err_gbe_bin) - b0rk(EIO, "%s: error writing final file"); + err_exit(EIO, "%s: error writing final file"); free_and_set_null(&f->tname); return EXIT_SUCCESS; } + +static void +exit_cleanup(void) +{ + return; +} |
