diff options
Diffstat (limited to 'util/nvmutil')
| -rw-r--r-- | util/nvmutil/nvmutil.c | 268 |
1 files changed, 161 insertions, 107 deletions
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c index abe94904..6b7be3df 100644 --- a/util/nvmutil/nvmutil.c +++ b/util/nvmutil/nvmutil.c @@ -47,9 +47,6 @@ * TODO: bound checks for files per-command, e.g. only * first 6 bytes for CMD_SETMAC * - * TODO: clean up the do_rw function: make PSCHREIB and - * so on clearer, probably just define them inline and - * validate them inline (no define). * TODO: in command sanitizer: verify that each given * entry corresponds to the correct function, in the * pointer (this check is currently missing) @@ -85,10 +82,6 @@ * TODO: also document the layout of Intel GbE files, so * that wily individuals can easily expand the * featureset of nvmutil. - * TODO: remove some clever code, e.g.: - * rw_type == PLESEN << 2 - * make stuff like that clearer. - * ditto the invert copy/swap trick * TODO: write a manpage * TODO: simplify the command sanitization, implement more * of it as build time checks, e.g. static asserts. @@ -184,6 +177,7 @@ typedef unsigned int uint32_t; #include <time.h> #include <unistd.h> +/* type asserts */ typedef char static_assert_char_is_8_bits[(CHAR_BIT == 8) ? 1 : -1]; typedef char static_assert_uint8_is_1[(sizeof(uint8_t) == 1) ? 1 : -1]; typedef char static_assert_uint16_is_2[(sizeof(uint16_t) == 2) ? 1 : -1]; @@ -241,8 +235,6 @@ typedef char static_assert_off_t_is_32[(sizeof(off_t) >= 4) ? 1 : -1]; */ static void sanitize_command_list(void); static void sanitize_command_index(size_t c); -static void check_enum_bin(size_t a, const char *a_name, - size_t b, const char *b_name); /* * Argument handling (user input) @@ -351,6 +343,8 @@ static ssize_t do_rw(int fd, static ssize_t prw(int fd, void *mem, size_t nrw, off_t off, int rw_type); static off_t lseek_eintr(int fd, off_t off, int whence); +static int io_args(int fd, size_t nrw, + off_t off, int rw_type); /* * Error handling and cleanup @@ -388,6 +382,8 @@ static void usage(uint8_t usage_exit); #define NVM_WORDS (NVM_SIZE >> 1) #define NVM_CHECKSUM_WORD (NVM_WORDS - 1) +#define NUM_RANDOM_BYTES 12 + /* * Portable macro based on BSD nitems. * Used to count the number of commands (see below). @@ -441,10 +437,10 @@ static const char *argv0; #define ARGC_4 4 enum { - LESEN, - PLESEN, - SCHREIB, - PSCHREIB + IO_READ, + IO_WRITE, + IO_PREAD, + IO_PWRITE }; /* @@ -565,8 +561,40 @@ static const struct commands command[] = { */ static size_t cmd_index = CMD_NULL; +/* + * asserts (variables/defines sanity check) + */ typedef char assert_argc3[(ARGC_3==3)?1:-1]; typedef char assert_argc4[(ARGC_4==4)?1:-1]; +typedef char assert_read[(IO_READ==0)?1:-1]; +typedef char assert_write[(IO_WRITE==1)?1:-1]; +typedef char assert_pread[(IO_PREAD==2)?1:-1]; +typedef char assert_pwrite[(IO_PWRITE==3)?1:-1]; +typedef char assert_rand_byte[(NUM_RANDOM_BYTES>0)?1:-1]; +typedef char assert_rand_len[(NUM_RANDOM_BYTES<NVM_SIZE)?1:-1]; +/* commands */ +typedef char assert_cmd_dump[(CMD_DUMP==0)?1:-1]; +typedef char assert_cmd_setmac[(CMD_SETMAC==1)?1:-1]; +typedef char assert_cmd_swap[(CMD_SWAP==2)?1:-1]; +typedef char assert_cmd_copy[(CMD_COPY==3)?1:-1]; +typedef char assert_cmd_cat[(CMD_CAT==4)?1:-1]; +typedef char assert_cmd_cat16[(CMD_CAT16==5)?1:-1]; +typedef char assert_cmd_cat128[(CMD_CAT128==6)?1:-1]; +/* mod_type */ +typedef char assert_mod_off[(SET_MOD_OFF==0)?1:-1]; +typedef char assert_mod_0[(SET_MOD_0==1)?1:-1]; +typedef char assert_mod_1[(SET_MOD_1==2)?1:-1]; +typedef char assert_mod_n[(SET_MOD_N==3)?1:-1]; +typedef char assert_mod_both[(SET_MOD_BOTH==4)?1:-1]; +/* bool */ +typedef char bool_arg_nopart[(ARG_NOPART==0)?1:-1]; +typedef char bool_arg_part[(ARG_PART==1)?1:-1]; +typedef char bool_skip_checksum_read[(SKIP_CHECKSUM_READ==0)?1:-1]; +typedef char bool_checksum_read[(CHECKSUM_READ==1)?1:-1]; +typedef char bool_skip_checksum_write[(SKIP_CHECKSUM_WRITE==0)?1:-1]; +typedef char bool_checksum_write[(CHECKSUM_WRITE==1)?1:-1]; +typedef char bool_no_invert[(NO_INVERT==0)?1:-1]; +typedef char bool_part_invert[(PART_INVERT==1)?1:-1]; static int use_prng = 0; @@ -660,6 +688,9 @@ sanitize_command_list(void) sanitize_command_index(c); } +/* + * TODO: specific config checks per command + */ static void sanitize_command_index(size_t c) { @@ -685,15 +716,6 @@ sanitize_command_index(size_t c) (unsigned long)c, command[c].str); } - if (!((CMD_SETMAC > CMD_DUMP) && (CMD_SWAP > CMD_SETMAC) && - (CMD_COPY > CMD_SWAP) && (CMD_CAT > CMD_COPY) && - (CMD_CAT16 > CMD_CAT) && (CMD_CAT128 > CMD_CAT16))) - err(EINVAL, "Some command integers are the same"); - - if (!((SET_MOD_0 > SET_MOD_OFF) && (SET_MOD_1 > SET_MOD_0) && - (SET_MOD_N > SET_MOD_1) && (SET_MOD_BOTH > SET_MOD_N))) - err(EINVAL, "Some modtype integers are the same"); - mod_type = command[c].set_modified; switch (mod_type) { case SET_MOD_0: @@ -711,13 +733,6 @@ sanitize_command_index(size_t c) check_bin(command[c].chksum_read, "cmd.chksum_read"); check_bin(command[c].chksum_write, "cmd.chksum_write"); - check_enum_bin(ARG_NOPART, "ARG_NOPART", ARG_PART, "ARG_PART"); - check_enum_bin(SKIP_CHECKSUM_READ, "SKIP_CHECKSUM_READ", - CHECKSUM_READ, "CHECKSUM_READ"); - check_enum_bin(SKIP_CHECKSUM_WRITE, "SKIP_CHECKSUM_WRITE", - CHECKSUM_WRITE, "CHECKSUM_WRITE"); - check_enum_bin(NO_INVERT, "NO_INVERT", PART_INVERT, "PART_INVERT"); - gbe_rw_size = command[c].rw_size; switch (gbe_rw_size) { @@ -736,20 +751,6 @@ sanitize_command_index(size_t c) if (command[c].flags != O_RDONLY && command[c].flags != O_RDWR) err(EINVAL, "invalid cmd.flags setting"); - - if (!((!LESEN) && (PLESEN == 1) && (SCHREIB == 2) && (PSCHREIB == 3))) - err(EINVAL, "rw type integers are the wrong values"); -} - -static void -check_enum_bin(size_t a, const char *a_name, - size_t b, const char *b_name) -{ - if (a) - err(EINVAL, "%s is non-zero", a_name); - - if (b != 1) - err(EINVAL, "%s is a value other than 1", b_name); } static void @@ -906,7 +907,7 @@ read_gbe_file(void) for (p = 0; p < 2; p++) { if (do_read[p]) - rw_gbe_file_part(p, PLESEN, "pread"); + rw_gbe_file_part(p, IO_PREAD, "pread"); } } @@ -1131,14 +1132,14 @@ static uint16_t rhex(void) { static size_t n = 0; - static uint8_t rnum[12]; + static uint8_t rnum[NUM_RANDOM_BYTES]; if (use_prng) return fallback_rand(); if (!n) { n = sizeof(rnum); - if (rw_file_exact(urandom_fd, rnum, n, 0, LESEN) == -1) + if (rw_file_exact(urandom_fd, rnum, n, 0, IO_READ) == -1) err(errno, "Randomisation failed"); } @@ -1177,6 +1178,7 @@ entropy_jitter(void) { struct timeval a, b; unsigned long mix = 0; + long mix_diff; int i; for (i = 0; i < 8; i++) { @@ -1184,7 +1186,15 @@ entropy_jitter(void) getpid(); gettimeofday(&b, NULL); - mix ^= (unsigned long)(b.tv_usec - a.tv_usec); + /* + * prevent negative numbers to prevent overflow, + * which would bias rand to large numbers + */ + mix_diff = (long)(b.tv_usec - a.tv_usec); + if (mix_diff < 0) + mix_diff = -mix_diff; + + mix ^= (unsigned long)(mix_diff); mix ^= (unsigned long)&mix; } @@ -1239,7 +1249,9 @@ print_mac_from_nvm(size_t partnum) for (c = 0; c < 3; c++) { val16 = nvm_word(c, partnum); - printf("%02x:%02x", val16 & 0xff, val16 >> 8); + printf("%02x:%02x", + (unsigned int)(val16 & 0xff), + (unsigned int)(val16 >> 8)); if (c == 2) printf("\n"); else @@ -1260,7 +1272,9 @@ hexdump(size_t partnum) val16 = nvm_word((row << 3) + c, partnum); if (c == 4) printf(" "); - printf(" %02x %02x", val16 & 0xff, val16 >> 8); + printf(" %02x %02x", + (unsigned int)(val16 & 0xff), + (unsigned int)(val16 >> 8)); } printf("\n"); } @@ -1297,7 +1311,7 @@ gbe_cat_buf(uint8_t *b) while (1) { rval = rw_file_exact(STDOUT_FILENO, b, - GBE_PART_SIZE, 0, SCHREIB); + GBE_PART_SIZE, 0, IO_WRITE); if (rval >= 0) { /* @@ -1338,7 +1352,7 @@ write_gbe_file(void) if (update_checksum) set_checksum(partnum); - rw_gbe_file_part(partnum, PSCHREIB, "pwrite"); + rw_gbe_file_part(partnum, IO_PWRITE, "pwrite"); } } @@ -1462,7 +1476,11 @@ rw_gbe_file_part(size_t p, int rw_type, uint8_t *mem_offset; - if (rw_type == SCHREIB || rw_type == PSCHREIB) + if (rw_type < IO_PREAD || rw_type > IO_PWRITE) + err(errno, "%s: %s: part %lu: invalid rw_type, %d", + fname, rw_type_str, (unsigned long)p, rw_type); + + if (rw_type == IO_PWRITE) invert = 0; /* @@ -1532,7 +1550,7 @@ gbe_x_offset(size_t p, const char *f_op, const char *d_type, /* * Read or write the exact contents of a file, * along with a buffer, (if applicable) offset, - * and number of bytes to be read. It unified + * and number of bytes to be read. It unifies * the functionality of read(), pread(), write() * and pwrite(), with retry-on-EINTR and also * prevents infinite loop on zero-reads. @@ -1545,105 +1563,86 @@ gbe_x_offset(size_t p, const char *f_op, const char *d_type, * be used on sockets or pipes, because 0-byte * reads are treated like fatal errors. This * means that EOF is also considered fatal. - * - * WARNING: Do not use O_APPEND on open() when - * using this function. If you do, POSIX allows - * write() to ignore the current file offset and - * write at EOF, which means that our use of - * lseek in prw() does not guarantee writing at - * a specified offset. So if using PSCHREIB or - * PLESEN, make sure not to pass a file descriptor - * with the O_APPEND flag. Alternatively, modify - * do_rw() to directly use pwrite() and pread() - * instead of prw(). */ static ssize_t -rw_file_exact(int fd, uint8_t *mem, size_t len, +rw_file_exact(int fd, uint8_t *mem, size_t nrw, off_t off, int rw_type) { ssize_t rv; size_t rc; - if (fd < 0 || !len || len > (size_t)SSIZE_MAX) { + if (io_args(fd, nrw, off, rw_type) == -1) { errno = EIO; return -1; } - for (rc = 0, rv = 0; rc < len; rc += (size_t)rv) { - if ((rv = rw_file_once(fd, mem, len, off, rw_type, rc)) == -1) + for (rc = 0, rv = 0; rc < nrw; ) { + if ((rv = rw_file_once(fd, mem, nrw, off, rw_type, rc)) <= 0) return -1; + + rc += (size_t)rv; } return rc; } +/* + * May not return all requested bytes (len). + * Use rw_file_exact for guaranteed length. + */ static ssize_t -rw_file_once(int fd, uint8_t *mem, size_t len, +rw_file_once(int fd, uint8_t *mem, size_t nrw, off_t off, int rw_type, size_t rc) { ssize_t rv; size_t retries_on_zero = 0; size_t max_retries = 10; + + if (io_args(fd, nrw, off, rw_type) == -1) + goto err_rw_file_once; + read_again: - rv = do_rw(fd, mem + rc, len - rc, off + rc, rw_type); + rv = do_rw(fd, mem + rc, nrw - rc, off + rc, rw_type); if (rv < 0 && errno == EINTR) goto read_again; - if (rv < 0) { - errno = EIO; + if (rv < 0) return -1; - } - /* - * Theoretical bug: if a buggy libc returned - * a size larger than SSIZE_MAX, the cast may - * cause an overflow. Specifications guarantee - * this won't happen, but spec != implementation - */ - if ((size_t)rv > SSIZE_MAX) { - errno = EIO; - return -1; - /* we do not tolerate buggy libc */ - } + if ((size_t)rv > SSIZE_MAX /* theoretical buggy libc */ + || (size_t)rv > (nrw - rc))/* don't overflow */ + goto err_rw_file_once; - if (!((size_t)rv > (len - rc) /* don't overflow */ - || rv == 0)) + if (rv != 0) return rv; - /* Prevent infinite 0-byte loop */ - if (rv != 0) { - errno = EIO; - return -1; - } - /* - * Fault tolerance against infinite - * zero-byte loop: re-try a finite - * number of times. This mitigates - * otherwise OK but slow filesystems - * e.g. NFS or slow media. - */ if (retries_on_zero++ < max_retries) goto read_again; +err_rw_file_once: errno = EIO; return -1; } static ssize_t do_rw(int fd, uint8_t *mem, - size_t len, off_t off, int rw_type) + size_t nrw, off_t off, int rw_type) { - if (rw_type == LESEN || rw_type == PLESEN << 2) - return read(fd, mem, len); + if (io_args(fd, nrw, off, rw_type) == -1) + goto err_do_rw; - if (rw_type == SCHREIB || rw_type == PSCHREIB << 2) - return write(fd, mem, len); + if (rw_type == IO_READ) + return read(fd, mem, nrw); - if (rw_type == PLESEN || rw_type == PSCHREIB) - return prw(fd, mem, len, off, rw_type); + if (rw_type == IO_WRITE) + return write(fd, mem, nrw); - errno = EINVAL; + if (rw_type == IO_PREAD || rw_type == IO_PWRITE) + return prw(fd, mem, nrw, off, rw_type); + +err_do_rw: + errno = EIO; return -1; } @@ -1663,25 +1662,80 @@ prw(int fd, void *mem, size_t nrw, off_t off_orig; ssize_t r; int saved_errno; + int prw_type; + int flags; + + if (io_args(fd, nrw, off, rw_type) == -1) + goto err_prw; + + prw_type = rw_type ^ IO_PREAD; + + if ((unsigned int)prw_type > IO_WRITE) + goto err_prw; + + flags = fcntl(fd, F_GETFL); + if (flags == -1) + return -1; + + /* + * O_APPEND must not be used, because this + * allows POSIX write() to ignore the + * current write offset and write at EOF, + * which would therefore break pread/pwrite + */ + if (flags & O_APPEND) + goto err_prw; if ((off_orig = lseek_eintr(fd, (off_t)0, SEEK_CUR)) == (off_t)-1) return -1; + if (lseek_eintr(fd, off, SEEK_SET) == (off_t)-1) return -1; do { - r = do_rw(fd, mem, nrw, off, rw_type << 2); + r = do_rw(fd, mem, nrw, off, prw_type); } while (r < 0 && errno == EINTR); saved_errno = errno; if (lseek_eintr(fd, off_orig, SEEK_SET) == (off_t)-1) { if (r < 0) errno = saved_errno; + return -1; } errno = saved_errno; return r; + +err_prw: + errno = EIO; + return -1; +} + +static int +io_args(int fd, size_t nrw, + off_t off, int rw_type) +{ + if (off != 0 + && off != gbe_file_offset(1, "i/o check")) + goto err_io_args; + + if (nrw != GBE_PART_SIZE && + nrw != NVM_SIZE && + nrw != NUM_RANDOM_BYTES) + goto err_io_args; + + if (fd < 0 + || !nrw /* prevent zero read request */ + || nrw > (size_t)SSIZE_MAX /* prevent overflow */ + || (unsigned int)rw_type > IO_PWRITE) + goto err_io_args; + + return 0; + +err_io_args: + errno = EIO; + return -1; } static off_t |
