summaryrefslogtreecommitdiff
path: root/util/nvmutil
diff options
context:
space:
mode:
Diffstat (limited to 'util/nvmutil')
-rw-r--r--util/nvmutil/nvmutil.c289
1 files changed, 169 insertions, 120 deletions
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c
index b718ac6e..da7c426c 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,22 +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) {
- /*
- * On very old Unix systems that
- * lack /dev/random and /dev/urandom
- */
+ if (use_prng)
return fallback_rand();
- }
-
- if (urandom_fd < 0)
- err(ECANCELED, "Your operating system has no /dev/[u]random");
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");
}
@@ -1185,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++) {
@@ -1192,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;
}
@@ -1247,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
@@ -1268,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");
}
@@ -1305,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) {
/*
@@ -1346,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");
}
}
@@ -1470,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;
/*
@@ -1540,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.
@@ -1553,102 +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) {
+ if (rv < 0 && errno == EINTR)
goto read_again;
- } else if (rv < 0) {
- errno = EIO;
- 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;
+ if (rv < 0)
return -1;
- /* we will not tolerate your buggy libc */
- }
- if ((size_t)rv > (len - rc) /* Prevent overflow */
- || rv == 0) { /* Prevent infinite 0-byte loop */
- if (rv == 0) {
- /*
- * 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;
- }
- errno = EIO;
- return -1;
- }
+ if ((size_t)rv > SSIZE_MAX /* theoretical buggy libc */
+ || (size_t)rv > (nrw - rc))/* don't overflow */
+ goto err_rw_file_once;
+
+ if (rv != 0)
+ return rv;
+
+ if (retries_on_zero++ < max_retries)
+ goto read_again;
- return rv;
+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;
}
@@ -1668,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_PART_SIZE)
+ 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