summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
Diffstat (limited to 'util')
-rw-r--r--util/nvmutil/nvmutil.c797
1 files changed, 548 insertions, 249 deletions
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c
index da6336aa..62eaa6c5 100644
--- a/util/nvmutil/nvmutil.c
+++ b/util/nvmutil/nvmutil.c
@@ -22,7 +22,7 @@
* Make most of nvmutil a *library* for re-use
*
* TODO: gettimeofday not posible - use portable functions.
- * TODO: uint32_t fallback: modify the program instead
+ * TODO: ux fallback: modify the program instead
* to run on 16-bit systems: smaller buffers, and do
* operations byte-based instead of word-based.
*
@@ -135,11 +135,12 @@ CFLAGS += -fstack-protector-strong
CFLAGS += -fno-common
CFLAGS += -D_FORTIFY_SOURCE=2
CFLAGS += -fPIE
-*/
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 500
-#endif
+also consider:
+-fstack-clash-protection
+-Wl,-z,relro
+-Wl,-z,now
+*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
@@ -156,31 +157,29 @@ CFLAGS += -fPIE
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
-#if defined(__has_include)
-#if __has_include(<stdint.h>)
-#include <stdint.h>
-#else
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int uint32_t;
-#endif
-#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
-#include <stdint.h>
-#else
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int uint32_t;
-#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
+typedef unsigned char u8;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+/* 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];
-typedef char static_assert_uint32_is_4[(sizeof(uint32_t) == 4) ? 1 : -1];
+typedef char static_assert_char_is_1[(sizeof(char) == 1) ? 1 : -1];
+typedef char static_assert_u8_is_1[
+ (sizeof(u8) == 1) ? 1 : -1];
+typedef char static_assert_ushort_is_2[
+ (sizeof(ushort) >= 2) ? 1 : -1];
+typedef char static_assert_short_is_2[(sizeof(short) >= 2) ? 1 : -1];
+typedef char static_assert_uint_is_4[
+ (sizeof(uint) >= 4) ? 1 : -1];
+typedef char static_assert_ulong_is_4[
+ (sizeof(ulong) >= 4) ? 1 : -1];
typedef char static_assert_int_ge_32[(sizeof(int) >= 4) ? 1 : -1];
typedef char static_assert_twos_complement[
((-1 & 3) == 3) ? 1 : -1
@@ -190,6 +189,9 @@ typedef char static_assert_twos_complement[
* We set _FILE_OFFSET_BITS 64, but we only handle
* files that are 128KB in size at a maximum, so we
* realistically only need 32-bit at a minimum.
+ *
+ * We set 64 anyway, because there's no reason not
+ * to, but some systems may ignore _FILE_OFFSET_BITS
*/
typedef char static_assert_off_t_is_32[(sizeof(off_t) >= 4) ? 1 : -1];
@@ -225,8 +227,8 @@ typedef char static_assert_off_t_is_32[(sizeof(off_t) >= 4) ? 1 : -1];
#define O_BINARY 0
#endif
-#ifndef O_NONBLOCK
-#define O_NONBLOCK 0
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
#endif
/*
@@ -234,8 +236,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)
@@ -254,6 +254,7 @@ static int xstrxcmp(const char *a, const char *b, size_t maxlen);
*/
static void open_dev_urandom(void);
static void open_gbe_file(void);
+static void lock_gbe_file(void);
static void xopen(int *fd, const char *path, int flags, struct stat *st);
/*
@@ -272,7 +273,7 @@ static int good_checksum(size_t partnum);
*/
static void run_cmd(size_t c);
static void check_command_num(size_t c);
-static uint8_t valid_command(size_t c);
+static u8 valid_command(size_t c);
/*
* Helper functions for command: setmac
@@ -283,10 +284,10 @@ static size_t xstrxlen(const char *scmp, size_t maxlen);
static void set_mac_byte(size_t mac_byte_pos);
static void set_mac_nib(size_t mac_str_pos,
size_t mac_byte_pos, size_t mac_nib_pos);
-static uint16_t hextonum(char ch_s);
-static uint16_t rhex(void);
-static uint16_t fallback_rand(void);
-static unsigned long entropy_jitter(void);
+static ushort hextonum(char ch_s);
+static ushort rhex(void);
+static ushort fallback_rand(void);
+static ulong entropy_jitter(void);
static void write_mac_part(size_t partnum);
/*
@@ -301,7 +302,7 @@ static void hexdump(size_t partnum);
* cat, cat16 and cat128
*/
static void cmd_helper_cat(void);
-static void gbe_cat_buf(uint8_t *b);
+static void gbe_cat_buf(u8 *b);
/*
* After command processing, write
@@ -313,14 +314,14 @@ static void gbe_cat_buf(uint8_t *b);
static void write_gbe_file(void);
static void override_part_modified(void);
static void set_checksum(size_t part);
-static uint16_t calculated_checksum(size_t p);
+static ushort calculated_checksum(size_t p);
/*
* Helper functions for accessing
* the NVM area during operation.
*/
-static uint16_t nvm_word(size_t pos16, size_t part);
-static void set_nvm_word(size_t pos16, size_t part, uint16_t val16);
+static ushort nvm_word(size_t pos16, size_t part);
+static void set_nvm_word(size_t pos16, size_t part, ushort val16);
static void set_part_modified(size_t p);
static void check_nvm_bound(size_t pos16, size_t part);
static void check_bin(size_t a, const char *a_name);
@@ -331,19 +332,25 @@ static void check_bin(size_t a, const char *a_name);
*/
static void rw_gbe_file_part(size_t p, int rw_type,
const char *rw_type_str);
-static uint8_t *gbe_mem_offset(size_t part, const char *f_op);
+static void check_written_part(size_t p);
+static void report_io_err_rw(void);
+static u8 *gbe_mem_offset(size_t part, const char *f_op);
static off_t gbe_file_offset(size_t part, const char *f_op);
static off_t gbe_x_offset(size_t part, const char *f_op,
const char *d_type, off_t nsize, off_t ncmp);
-static ssize_t rw_file_exact(int fd, uint8_t *mem, size_t len,
+static ssize_t rw_gbe_file_exact(int fd, u8 *mem, size_t nrw,
off_t off, int rw_type);
-static ssize_t rw_file_once(int fd, uint8_t *mem, size_t len,
- off_t off, int rw_type, size_t rc);
-static ssize_t do_rw(int fd,
- uint8_t *mem, size_t len, off_t off, int rw_type);
+static ssize_t rw_file_exact(int fd, u8 *mem, size_t len,
+ off_t off, int rw_type, int loop_eagain, int loop_eintr);
+static ssize_t rw_file_once(int fd, u8 *mem, size_t len,
+ off_t off, int rw_type, size_t rc, int loop_eagain,
+ int loop_eintr);
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);
+ off_t off, int rw_type, int loop_eagain, int loop_eintr);
+static int rw_over_nrw(ssize_t r, size_t nrw);
+static off_t lseek_loop(int fd, off_t off,
+ int whence, int loop_eagain, int loop_eintr);
+static int try_err(int loop_err, int errval);
/*
* Error handling and cleanup
@@ -351,7 +358,7 @@ static off_t lseek_eintr(int fd, off_t off, int whence);
static void err(int nvm_errval, const char *msg, ...);
static void close_files(void);
static const char *getnvmprogname(void);
-static void usage(uint8_t usage_exit);
+static void usage(int usage_exit);
/*
* Sizes in bytes:
@@ -381,6 +388,9 @@ 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
+static u8 rnum[NUM_RANDOM_BYTES];
+
/*
* Portable macro based on BSD nitems.
* Used to count the number of commands (see below).
@@ -399,17 +409,18 @@ static const char *rname = NULL;
*
* The code will handle this properly.
*/
-static uint8_t buf[GBE_FILE_SIZE];
-static uint8_t pad[GBE_PART_SIZE]; /* the file that wouldn't die */
+static u8 real_buf[GBE_FILE_SIZE];
+static u8 pad[GBE_FILE_SIZE]; /* the file that wouldn't die */
+static u8 *buf = real_buf;
-static uint16_t mac_buf[3];
+static ushort mac_buf[3];
static off_t gbe_file_size;
static int urandom_fd = -1;
static int gbe_fd = -1;
static size_t part;
-static uint8_t part_modified[2];
-static uint8_t part_valid[2];
+static u8 part_modified[2];
+static u8 part_valid[2];
static const char rmac[] = "xx:xx:xx:xx:xx:xx";
static const char *mac_str;
@@ -486,11 +497,11 @@ struct commands {
const char *str;
void (*run)(void);
int argc;
- uint8_t invert;
- uint8_t set_modified;
- uint8_t arg_part;
- uint8_t chksum_read;
- uint8_t chksum_write;
+ u8 invert;
+ u8 set_modified;
+ u8 arg_part;
+ u8 chksum_read;
+ u8 chksum_write;
size_t rw_size; /* within the 4KB GbE part */
int flags; /* e.g. O_RDWR or O_RDONLY */
};
@@ -558,15 +569,50 @@ 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;
+static int io_err_gbe = 0;
+static int rw_check_err_read[] = {0, 0};
+static int rw_check_partial_read[] = {0, 0};
+static int rw_check_bad_part[] = {0, 0};
+
+static int post_rw_checksum[] = {0, 0};
+
int
main(int argc, char *argv[])
{
@@ -621,6 +667,7 @@ main(int argc, char *argv[])
open_dev_urandom();
open_gbe_file();
+ lock_gbe_file();
#ifdef NVMUTIL_PLEDGE
if (pledge("stdio", NULL) == -1)
@@ -637,9 +684,27 @@ main(int argc, char *argv[])
run_cmd(cmd_index);
- if (command[cmd_index].flags == O_RDWR)
+ if (command[cmd_index].flags == O_RDWR) {
+
write_gbe_file();
+ /*
+ * We may otherwise read from
+ * cache, so we must sync.
+ */
+ if (fsync(gbe_fd) == -1)
+ err(errno, "%s: fsync (pre-verification)",
+ fname);
+
+ check_written_part(0);
+ check_written_part(1);
+
+ report_io_err_rw();
+
+ if (io_err_gbe)
+ err(EIO, "%s: bad write", fname);
+ }
+
close_files();
return EXIT_SUCCESS;
@@ -657,40 +722,34 @@ sanitize_command_list(void)
sanitize_command_index(c);
}
+/*
+ * TODO: specific config checks per command
+ */
static void
sanitize_command_index(size_t c)
{
- uint8_t mod_type;
+ u8 mod_type;
size_t gbe_rw_size;
check_command_num(c);
if (command[c].argc < 3)
err(EINVAL, "cmd index %lu: argc below 3, %d",
- (unsigned long)c, command[c].argc);
+ (ulong)c, command[c].argc);
if (command[c].str == NULL)
err(EINVAL, "cmd index %lu: NULL str",
- (unsigned long)c);
+ (ulong)c);
if (*command[c].str == '\0')
err(EINVAL, "cmd index %lu: empty str",
- (unsigned long)c);
+ (ulong)c);
if (xstrxlen(command[c].str, MAX_CMD_LEN + 1) >
MAX_CMD_LEN) {
err(EINVAL, "cmd index %lu: str too long: %s",
- (unsigned long)c, command[c].str);
+ (ulong)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:
@@ -708,13 +767,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) {
@@ -723,12 +775,12 @@ sanitize_command_index(size_t c)
break;
default:
err(EINVAL, "Unsupported rw_size: %lu",
- (unsigned long)gbe_rw_size);
+ (ulong)gbe_rw_size);
}
if (gbe_rw_size > GBE_PART_SIZE)
err(EINVAL, "rw_size larger than GbE part: %lu",
- (unsigned long)gbe_rw_size);
+ (ulong)gbe_rw_size);
if (command[c].flags != O_RDONLY &&
command[c].flags != O_RDWR)
@@ -736,17 +788,6 @@ 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)
-{
- 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
set_cmd(int argc, char *argv[])
{
const char *cmd_str;
@@ -768,7 +809,7 @@ set_cmd(int argc, char *argv[])
static void
set_cmd_args(int argc, char *argv[])
{
- uint8_t arg_part;
+ u8 arg_part;
if (!valid_command(cmd_index) || argc < 3)
usage(1);
@@ -792,13 +833,13 @@ set_cmd_args(int argc, char *argv[])
static size_t
conv_argv_part_num(const char *part_str)
{
- unsigned char ch;
+ u8 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];
+ ch = (u8)part_str[0];
if (ch < '0' || ch > '1')
err(EINVAL, "Bad part number (%c)", ch);
@@ -822,7 +863,7 @@ xstrxcmp(const char *a, const char *b, size_t maxlen)
for (i = 0; i < maxlen; i++) {
if (a[i] != b[i])
- return (unsigned char)a[i] - (unsigned char)b[i];
+ return (u8)a[i] - (u8)b[i];
if (a[i] == '\0')
return 0;
@@ -850,7 +891,7 @@ open_dev_urandom(void)
/* fallback on VERY VERY VERY old unix */
use_prng = 1;
- srand((unsigned)(time(NULL) ^ getpid()));
+ srand((uint)(time(NULL) ^ getpid()));
}
static void
@@ -858,7 +899,8 @@ open_gbe_file(void)
{
struct stat gbe_st;
- xopen(&gbe_fd, fname, command[cmd_index].flags | O_BINARY, &gbe_st);
+ xopen(&gbe_fd, fname,
+ command[cmd_index].flags | O_BINARY | O_NOFOLLOW, &gbe_st);
gbe_file_size = gbe_st.st_size;
@@ -873,6 +915,24 @@ open_gbe_file(void)
}
static void
+lock_gbe_file(void)
+{
+ struct flock fl;
+
+ memset(&fl, 0, sizeof(fl));
+
+ if (command[cmd_index].flags == O_RDONLY)
+ fl.l_type = F_RDLCK;
+ else
+ fl.l_type = F_WRLCK;
+
+ fl.l_whence = SEEK_SET;
+
+ if (fcntl(gbe_fd, F_SETLK, &fl) == -1)
+ err(errno, "file is locked by another process");
+}
+
+static void
xopen(int *fd_ptr, const char *path, int flags, struct stat *st)
{
if ((*fd_ptr = open(path, flags)) == -1)
@@ -889,7 +949,7 @@ static void
read_gbe_file(void)
{
size_t p;
- uint8_t do_read[2] = {1, 1};
+ u8 do_read[2] = {1, 1};
/*
* Commands specifying a partnum only
@@ -909,10 +969,10 @@ read_checksums(void)
{
size_t p;
size_t skip_part;
- uint8_t invert;
- uint8_t arg_part;
- uint8_t num_invalid;
- uint8_t max_invalid;
+ u8 invert;
+ u8 arg_part;
+ u8 num_invalid;
+ u8 max_invalid;
part_valid[0] = 0;
part_valid[1] = 0;
@@ -949,7 +1009,7 @@ read_checksums(void)
if (num_invalid >= max_invalid) {
if (max_invalid == 1)
err(ECANCELED, "%s: part %lu has a bad checksum",
- fname, (unsigned long)part);
+ fname, (ulong)part);
err(ECANCELED, "%s: No valid checksum found in file",
fname);
}
@@ -958,8 +1018,8 @@ read_checksums(void)
static int
good_checksum(size_t partnum)
{
- uint16_t expected_checksum = calculated_checksum(partnum);
- uint16_t current_checksum = nvm_word(NVM_CHECKSUM_WORD, partnum);
+ ushort expected_checksum = calculated_checksum(partnum);
+ ushort current_checksum = nvm_word(NVM_CHECKSUM_WORD, partnum);
if (current_checksum == expected_checksum)
return 1;
@@ -980,10 +1040,10 @@ check_command_num(size_t c)
{
if (!valid_command(c))
err(EINVAL, "Invalid run_cmd arg: %lu",
- (unsigned long)c);
+ (ulong)c);
}
-static uint8_t
+static u8
valid_command(size_t c)
{
if (c >= N_COMMANDS)
@@ -991,7 +1051,7 @@ valid_command(size_t c)
if (c != command[c].chk)
err(EINVAL, "Invalid cmd chk value (%lu) vs arg: %lu",
- (unsigned long)command[c].chk, (unsigned long)c);
+ (ulong)command[c].chk, (ulong)c);
return 1;
}
@@ -1077,7 +1137,7 @@ set_mac_nib(size_t mac_str_pos,
size_t mac_byte_pos, size_t mac_nib_pos)
{
char mac_ch;
- uint16_t hex_num;
+ ushort hex_num;
mac_ch = mac_str[mac_str_pos + mac_nib_pos];
@@ -1102,17 +1162,17 @@ set_mac_nib(size_t mac_str_pos,
| ((mac_nib_pos ^ 1) << 2)); /* left or right nib? */
}
-static uint16_t
+static ushort
hextonum(char ch_s)
{
- unsigned char ch = (unsigned char)ch_s;
+ u8 ch = (u8)ch_s;
- if ((unsigned)(ch - '0') <= 9)
+ if ((uint)(ch - '0') <= 9)
return ch - '0';
ch |= 0x20;
- if ((unsigned)(ch - 'a') <= 5)
+ if ((uint)(ch - 'a') <= 5)
return ch - 'a' + 10;
if (ch == '?' || ch == 'x')
@@ -1121,37 +1181,36 @@ hextonum(char ch_s)
return 16; /* invalid character */
}
-static uint16_t
+static ushort
rhex(void)
{
static size_t n = 0;
- static uint8_t rnum[12];
if (use_prng)
return fallback_rand();
if (!n) {
n = sizeof(rnum);
- if (rw_file_exact(urandom_fd, rnum, n, 0, IO_READ) == -1)
+ if (rw_file_exact(urandom_fd, rnum, n, 0, IO_READ, 0, 1) == -1)
err(errno, "Randomisation failed");
}
- return (uint16_t)(rnum[--n] & 0xf);
+ return (ushort)(rnum[--n] & 0xf);
}
-static uint16_t
+static ushort
fallback_rand(void)
{
struct timeval tv;
- unsigned long mix;
- static unsigned long counter = 0;
+ ulong mix;
+ static ulong counter = 0;
gettimeofday(&tv, NULL);
- mix = (unsigned long)tv.tv_sec
- ^ (unsigned long)tv.tv_usec
- ^ (unsigned long)getpid()
- ^ (unsigned long)&mix
+ mix = (ulong)tv.tv_sec
+ ^ (ulong)tv.tv_usec
+ ^ (ulong)getpid()
+ ^ (ulong)&mix
^ counter++
^ entropy_jitter();
@@ -1159,18 +1218,19 @@ fallback_rand(void)
* Stack addresses can vary between
* calls, thus increasing entropy.
*/
- mix ^= (unsigned long)&mix;
- mix ^= (unsigned long)&tv;
- mix ^= (unsigned long)&counter;
+ mix ^= (ulong)&mix;
+ mix ^= (ulong)&tv;
+ mix ^= (ulong)&counter;
- return (uint16_t)(mix & 0xf);
+ return (ushort)(mix & 0xf);
}
-static unsigned long
+static ulong
entropy_jitter(void)
{
struct timeval a, b;
- unsigned long mix = 0;
+ ulong mix = 0;
+ long mix_diff;
int i;
for (i = 0; i < 8; i++) {
@@ -1178,8 +1238,16 @@ entropy_jitter(void)
getpid();
gettimeofday(&b, NULL);
- mix ^= (unsigned long)(b.tv_usec - a.tv_usec);
- mix ^= (unsigned long)&mix;
+ /*
+ * 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 ^= (ulong)(mix_diff);
+ mix ^= (ulong)&mix;
}
return mix;
@@ -1198,7 +1266,7 @@ write_mac_part(size_t partnum)
set_nvm_word(w, partnum, mac_buf[w]);
printf("Wrote MAC address to part %lu: ",
- (unsigned long)partnum);
+ (ulong)partnum);
print_mac_from_nvm(partnum);
}
@@ -1215,11 +1283,11 @@ cmd_helper_dump(void)
fprintf(stderr,
"BAD checksum %04x in part %lu (expected %04x)\n",
nvm_word(NVM_CHECKSUM_WORD, partnum),
- (unsigned long)partnum,
+ (ulong)partnum,
calculated_checksum(partnum));
printf("MAC (part %lu): ",
- (unsigned long)partnum);
+ (ulong)partnum);
print_mac_from_nvm(partnum);
hexdump(partnum);
}
@@ -1229,11 +1297,13 @@ static void
print_mac_from_nvm(size_t partnum)
{
size_t c;
- uint16_t val16;
+ ushort val16;
for (c = 0; c < 3; c++) {
val16 = nvm_word(c, partnum);
- printf("%02x:%02x", val16 & 0xff, val16 >> 8);
+ printf("%02x:%02x",
+ (uint)(val16 & 0xff),
+ (uint)(val16 >> 8));
if (c == 2)
printf("\n");
else
@@ -1246,15 +1316,17 @@ hexdump(size_t partnum)
{
size_t c;
size_t row;
- uint16_t val16;
+ ushort val16;
for (row = 0; row < 8; row++) {
- printf("%08lx ", (unsigned long)((size_t)row << 4));
+ printf("%08lx ", (ulong)((size_t)row << 4));
for (c = 0; c < 8; c++) {
val16 = nvm_word((row << 3) + c, partnum);
if (c == 4)
printf(" ");
- printf(" %02x %02x", val16 & 0xff, val16 >> 8);
+ printf(" %02x %02x",
+ (uint)(val16 & 0xff),
+ (uint)(val16 >> 8));
}
printf("\n");
}
@@ -1285,36 +1357,21 @@ cmd_helper_cat(void)
}
static void
-gbe_cat_buf(uint8_t *b)
+gbe_cat_buf(u8 *b)
{
- ssize_t rval;
-
- while (1) {
- rval = rw_file_exact(STDOUT_FILENO, b,
- GBE_PART_SIZE, 0, IO_WRITE);
-
- if (rval >= 0) {
- /*
- * A partial write is especially
- * fatal, as it should already be
- * prevented in rw_file_exact().
- */
- if ((size_t)rval != GBE_PART_SIZE)
- err(EIO, "stdout: cat: Partial write");
- break;
- }
-
- if (errno != EAGAIN)
- err(errno, "stdout: cat");
- }
+ if (rw_file_exact(STDOUT_FILENO, b,
+ GBE_PART_SIZE, 0, IO_WRITE, 1, 1) < 0)
+ err(errno, "stdout: cat");
}
static void
write_gbe_file(void)
{
+ struct stat gbe_st;
+
size_t p;
size_t partnum;
- uint8_t update_checksum;
+ u8 update_checksum;
if (command[cmd_index].flags == O_RDONLY)
return;
@@ -1322,6 +1379,15 @@ write_gbe_file(void)
update_checksum = command[cmd_index].chksum_write;
override_part_modified();
+
+ if (fstat(gbe_fd, &gbe_st) == -1)
+ err(errno, "%s: re-check", fname);
+
+ if (gbe_st.st_size != gbe_file_size)
+ err(errno, "%s: file size changed before write", fname);
+
+ if (!S_ISREG(gbe_st.st_mode))
+ err(errno, "%s: file type changed before write", fname);
for (p = 0; p < 2; p++) {
partnum = p ^ command[cmd_index].invert;
@@ -1339,7 +1405,7 @@ write_gbe_file(void)
static void
override_part_modified(void)
{
- uint8_t mod_type = command[cmd_index].set_modified;
+ u8 mod_type = command[cmd_index].set_modified;
switch (mod_type) {
case SET_MOD_0:
@@ -1370,16 +1436,16 @@ set_checksum(size_t p)
set_nvm_word(NVM_CHECKSUM_WORD, p, calculated_checksum(p));
}
-static uint16_t
+static ushort
calculated_checksum(size_t p)
{
size_t c;
- uint32_t val16 = 0;
+ uint val16 = 0;
for (c = 0; c < NVM_CHECKSUM_WORD; c++)
- val16 += (uint32_t)nvm_word(c, p);
+ val16 += (uint)nvm_word(c, p);
- return (uint16_t)((NVM_CHECKSUM - val16) & 0xffff);
+ return (ushort)((NVM_CHECKSUM - val16) & 0xffff);
}
/*
@@ -1390,7 +1456,7 @@ calculated_checksum(size_t p)
* file, but we assume otherwise and adapt accordingly.
*/
-static uint16_t
+static ushort
nvm_word(size_t pos16, size_t p)
{
size_t pos;
@@ -1398,20 +1464,20 @@ nvm_word(size_t pos16, size_t p)
check_nvm_bound(pos16, p);
pos = (pos16 << 1) + (p * GBE_PART_SIZE);
- return (uint16_t)buf[pos] |
- ((uint16_t)buf[pos + 1] << 8);
+ return (ushort)buf[pos] |
+ ((ushort)buf[pos + 1] << 8);
}
static void
-set_nvm_word(size_t pos16, size_t p, uint16_t val16)
+set_nvm_word(size_t pos16, size_t p, ushort val16)
{
size_t pos;
check_nvm_bound(pos16, p);
pos = (pos16 << 1) + (p * GBE_PART_SIZE);
- buf[pos] = (uint8_t)(val16 & 0xff);
- buf[pos + 1] = (uint8_t)(val16 >> 8);
+ buf[pos] = (u8)(val16 & 0xff);
+ buf[pos + 1] = (u8)(val16 >> 8);
set_part_modified(p);
}
@@ -1436,7 +1502,7 @@ check_nvm_bound(size_t c, size_t p)
if (c >= NVM_WORDS)
err(ECANCELED, "check_nvm_bound: out of bounds %lu",
- (unsigned long)c);
+ (ulong)c);
}
static void
@@ -1444,19 +1510,25 @@ 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, (unsigned long)a);
+ a_name, (ulong)a);
}
static void
rw_gbe_file_part(size_t p, int rw_type,
const char *rw_type_str)
{
+ ssize_t r;
size_t gbe_rw_size = command[cmd_index].rw_size;
- uint8_t invert = command[cmd_index].invert;
+ u8 invert = command[cmd_index].invert;
- uint8_t *mem_offset;
+ u8 *mem_offset;
+ off_t file_offset;
- if (rw_type == IO_WRITE || rw_type == IO_PWRITE)
+ if (rw_type < IO_PREAD || rw_type > IO_PWRITE)
+ err(errno, "%s: %s: part %lu: invalid rw_type, %d",
+ fname, rw_type_str, (ulong)p, rw_type);
+
+ if (rw_type == IO_PWRITE)
invert = 0;
/*
@@ -1464,12 +1536,100 @@ rw_gbe_file_part(size_t p, int rw_type,
* E.g. read from p0 (file) to p1 (mem).
*/
mem_offset = gbe_mem_offset(p ^ invert, rw_type_str);
+ file_offset = (off_t)gbe_file_offset(p, rw_type_str);
+
+ r = rw_gbe_file_exact(gbe_fd, mem_offset,
+ gbe_rw_size, file_offset, rw_type);
- if (rw_file_exact(gbe_fd, mem_offset,
- gbe_rw_size, gbe_file_offset(p, rw_type_str),
- rw_type) == -1)
+ if (r == -1)
err(errno, "%s: %s: part %lu",
- fname, rw_type_str, (unsigned long)p);
+ fname, rw_type_str, (ulong)p);
+
+ if ((size_t)r != gbe_rw_size)
+ err(EIO, "%s: partial %s: part %lu",
+ fname, rw_type_str, (ulong)p);
+}
+
+static void
+check_written_part(size_t p)
+{
+ ssize_t r;
+ size_t gbe_rw_size;
+ u8 *mem_offset;
+ off_t file_offset;
+ u8 *buf_restore;
+
+ if (!part_modified[p])
+ return;
+
+ gbe_rw_size = command[cmd_index].rw_size;
+
+ /* invert not needed for pwrite */
+ mem_offset = gbe_mem_offset(p, "pwrite");
+ file_offset = (off_t)gbe_file_offset(p, "pwrite");
+
+ r = rw_gbe_file_exact(gbe_fd, pad,
+ gbe_rw_size, file_offset, IO_PREAD);
+
+ if (r == -1)
+ rw_check_err_read[p] = io_err_gbe = 1;
+ else if ((size_t)r != gbe_rw_size)
+ rw_check_partial_read[p] = io_err_gbe = 1;
+ else if (memcmp(mem_offset, pad, gbe_rw_size) != 0)
+ rw_check_bad_part[0] = io_err_gbe = 1;
+
+ buf_restore = buf;
+ buf = pad;
+ post_rw_checksum[p] = good_checksum(0);
+ buf = buf_restore;
+}
+
+static void
+report_io_err_rw(void)
+{
+ size_t p;
+
+ if (!io_err_gbe)
+ return;
+
+ for (p = 0; p < 2; p++) {
+ if (!part_modified[p])
+ continue;
+
+ if (rw_check_err_read[p])
+ fprintf(stderr,
+ "%s: pread: p%lu (post-verification)\n",
+ fname, (ulong)p);
+ if (rw_check_partial_read[p])
+ fprintf(stderr,
+ "%s: partial pread: p%lu (post-verification)\n",
+ fname, (ulong)p);
+ if (rw_check_bad_part[p])
+ fprintf(stderr,
+ "%s: pwrite: corrupt write on p%lu\n",
+ fname, (ulong)p);
+
+ /*
+ * so that we can re-use main checksumming features
+ * correct part to read always part 0
+ */
+
+ fprintf(stderr, "%s: ", fname);
+
+ if (post_rw_checksum[p])
+ fprintf(stderr, "GOOD");
+ else
+ fprintf(stderr, "BAD");
+
+ fprintf(stderr, " checksum in p%lu on-disk.\n",
+ (ulong)p);
+
+ if (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");
+ }
+ }
}
/*
@@ -1477,13 +1637,13 @@ rw_gbe_file_part(size_t p, int rw_type,
* but used to check Gbe bounds in memory,
* and it is *also* used during file I/O.
*/
-static uint8_t *
+static u8 *
gbe_mem_offset(size_t p, const char *f_op)
{
off_t gbe_off = gbe_x_offset(p, f_op, "mem",
GBE_PART_SIZE, GBE_FILE_SIZE);
- return (uint8_t *)(buf + gbe_off);
+ return (u8 *)(buf + gbe_off);
}
/*
@@ -1523,10 +1683,38 @@ gbe_x_offset(size_t p, const char *f_op, const char *d_type,
return off;
}
+static ssize_t
+rw_gbe_file_exact(int fd, u8 *mem, size_t nrw,
+ off_t off, int rw_type)
+{
+ if (mem == NULL)
+ goto err_rw_gbe_file_exact;
+
+ if (mem != (void *)pad
+ && mem != (void *)rnum
+ && (mem < buf || mem >= (buf + GBE_FILE_SIZE)))
+ goto err_rw_gbe_file_exact;
+
+ if (off < 0 || off >= gbe_file_size)
+ goto err_rw_gbe_file_exact;
+
+ if (nrw > (size_t)(gbe_file_size - off))
+ goto err_rw_gbe_file_exact;
+
+ if (nrw > GBE_PART_SIZE)
+ goto err_rw_gbe_file_exact;
+
+ return rw_file_exact(fd, mem, nrw, off, rw_type, 0, 1);
+
+err_rw_gbe_file_exact:
+ errno = EIO;
+ return -1;
+}
+
/*
* 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.
@@ -1539,34 +1727,28 @@ 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 IO_PWRITE or
- * IO_PREAD, 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,
- off_t off, int rw_type)
+rw_file_exact(int fd, u8 *mem, size_t nrw,
+ off_t off, int rw_type, int loop_eagain,
+ int loop_eintr)
{
ssize_t rv;
size_t rc;
- if (fd < 0 || !len || len > (size_t)SSIZE_MAX
- || (unsigned int)rw_type > IO_PWRITE) {
- errno = EIO;
- return -1;
- }
-
- for (rc = 0, rv = 0; rc < len; ) {
- if ((rv = rw_file_once(fd, mem, len, off, rw_type, rc)) <= 0)
+ for (rc = 0, rv = 0; rc < nrw; ) {
+ if ((rv = rw_file_once(fd, mem, nrw, off, rw_type, rc,
+ loop_eagain, loop_eintr)) < 0)
+ return -1;
+
+ /* rw_file_once never returns
+ zero, but it's still logically
+ incorrect not to handle it here */
+
+ if (rv == 0) {
+ errno = EIO;
return -1;
+ }
rc += (size_t)rv;
}
@@ -1575,31 +1757,38 @@ rw_file_exact(int fd, uint8_t *mem, size_t len,
}
/*
- * May not return all requested bytes (len).
+ * Helper function for rw_file_exact, that
+ * also does extra error handling pertaining
+ * to GbE file offsets.
+ *
+ * May not return all requested bytes (nrw).
* Use rw_file_exact for guaranteed length.
+ *
+ * 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.
*/
static ssize_t
-rw_file_once(int fd, uint8_t *mem, size_t len,
- off_t off, int rw_type, size_t rc)
+rw_file_once(int fd, u8 *mem, size_t nrw,
+ off_t off, int rw_type, size_t rc,
+ int loop_eagain, int loop_eintr)
{
ssize_t rv;
size_t retries_on_zero = 0;
size_t max_retries = 10;
-read_again:
- if ((unsigned int)rw_type > IO_PWRITE)
+ if (mem == NULL)
goto err_rw_file_once;
- rv = do_rw(fd, mem + rc, len - rc, off + rc, rw_type);
-
- if (rv < 0 && errno == EINTR)
- goto read_again;
+read_again:
+ rv = prw(fd, mem + rc, nrw - rc, off + rc, rw_type,
+ loop_eagain, loop_eintr);
if (rv < 0)
return -1;
- if ((size_t)rv > SSIZE_MAX /* theoretical buggy libc */
- || (size_t)rv > (len - rc))/* don't overflow */
+ if ((size_t)rv > (nrw - rc))/* don't overflow */
goto err_rw_file_once;
if (rv != 0)
@@ -1613,24 +1802,9 @@ err_rw_file_once:
return -1;
}
-static ssize_t
-do_rw(int fd, uint8_t *mem,
- size_t len, off_t off, int rw_type)
-{
- if (rw_type == IO_READ)
- return read(fd, mem, len);
-
- if (rw_type == IO_WRITE)
- return write(fd, mem, len);
-
- if (rw_type == IO_PREAD || rw_type == IO_PWRITE)
- return prw(fd, mem, len, off, rw_type);
-
- errno = EIO;
- return -1;
-}
-
/*
+ * prw() - portable read-write
+ *
* This implements a portable analog of pwrite()
* and pread() - note that this version is not
* thread-safe (race conditions are possible on
@@ -1638,55 +1812,180 @@ do_rw(int fd, uint8_t *mem,
*
* This limitation is acceptable, since nvmutil is
* single-threaded. Portability is the main goal.
+ *
+ * A fallback is provided for regular read/write.
+ * rw_type can be IO_READ, IO_WRITE, IO_PREAD
+ * or IO_PWRITE
+ *
+ * loop_eagain does a retry loop on EAGAIN if set
+ * loop_eintr does a retry loop on EINTR if set
+ *
+ * Unlike the bare syscalls, prw() does security
+ * checks e.g. checks NULL strings, checks bounds,
+ * also mitigates a few theoretical libc bugs.
+ * It is designed for extremely safe single-threaded
+ * I/O on applications that need it.
*/
+
static ssize_t
prw(int fd, void *mem, size_t nrw,
- off_t off, int rw_type)
+ off_t off, int rw_type,
+ int loop_eagain, int loop_eintr)
{
off_t off_orig;
ssize_t r;
int saved_errno;
- int prw_type;
+ int flags;
+ int positional_rw;
- prw_type = rw_type ^ IO_PREAD;
+ if (mem == NULL)
+ goto err_prw;
- if ((unsigned int)prw_type > IO_WRITE) {
- errno = EIO;
- return -1;
+ if (fd < 0
+ || off < 0
+ || !nrw /* prevent zero read request */
+ || nrw > (size_t)SSIZE_MAX /* prevent overflow */
+ || (uint)rw_type > IO_PWRITE)
+ goto err_prw;
+
+ r = -1;
+
+ if (rw_type >= IO_PREAD)
+ positional_rw = 1; /* pread/pwrite */
+ else
+ positional_rw = 0; /* read/write */
+
+try_rw_again:
+
+ if (!positional_rw) {
+ if (rw_type == IO_WRITE)
+ r = write(fd, mem, nrw);
+ else if (rw_type == IO_READ)
+ r = read(fd, mem, nrw);
+
+ if (r == -1 && (errno == try_err(loop_eintr, EINTR)
+ || errno == try_err(loop_eagain, EAGAIN)))
+ goto try_rw_again;
+
+ return rw_over_nrw(r, nrw);
}
- 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)
+ 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_loop(fd, (off_t)0, SEEK_CUR,
+ loop_eagain, loop_eintr)) == (off_t)-1)
+ r = -1;
+ else if (lseek_loop(fd, off, SEEK_SET,
+ loop_eagain, loop_eintr) == (off_t)-1)
+ r = -1;
+
do {
- r = do_rw(fd, mem, nrw, off, prw_type);
- } while (r < 0 && errno == EINTR);
+ if (rw_type == IO_PREAD)
+ r = read(fd, mem, nrw);
+ else if (rw_type == IO_PWRITE)
+ r = write(fd, mem, nrw);
+
+ r = rw_over_nrw(r, nrw);
+ } while (r == -1 &&
+ (errno == try_err(loop_eintr, EINTR)
+ || errno == try_err(loop_eagain, EAGAIN)));
saved_errno = errno;
- if (lseek_eintr(fd, off_orig, SEEK_SET) == (off_t)-1) {
+ if (lseek_loop(fd, off_orig, SEEK_SET,
+ loop_eagain, loop_eintr) == (off_t)-1) {
if (r < 0)
errno = saved_errno;
return -1;
}
errno = saved_errno;
+ return rw_over_nrw(r, nrw);
+
+err_prw:
+ errno = EIO;
+ return -1;
+}
+
+/*
+ * POSIX can say whatever it wants.
+ * specification != implementation
+ */
+static int
+rw_over_nrw(ssize_t r, size_t 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_MAX, but
+ * spec != implementation
+ *
+ * Check this after using
+ * [p]read() or [p]write()
+ */
+ goto err_rw_over_nrw;
+ }
+
+ /*
+ * Theoretical buggy libc:
+ * Should never return a number of
+ * bytes above the requested length.
+ */
+ if ((size_t)r > nrw)
+ goto err_rw_over_nrw;
+
return r;
+
+err_rw_over_nrw:
+
+ errno = EIO;
+ return -1;
}
static off_t
-lseek_eintr(int fd, off_t off, int whence)
+lseek_loop(int fd, off_t off, int whence,
+ int loop_eagain, int loop_eintr)
{
- off_t old;
+ off_t old = -1;
do {
old = lseek(fd, off, whence);
- } while (old == (off_t)-1 && errno == EINTR);
+ } while (old == (off_t)-1 && (
+ errno == try_err(loop_eintr, EINTR) ||
+ errno == try_err(loop_eagain, EAGAIN)));
return old;
}
+static int
+try_err(int loop_err, int errval)
+{
+ if (loop_err)
+ return errval;
+
+ /* errno is never negative,
+ so functions checking it
+ can use it accordingly */
+ return -1;
+}
+
static void
err(int nvm_errval, const char *msg, ...)
{
@@ -1744,7 +2043,7 @@ getnvmprogname(void)
}
static void
-usage(uint8_t usage_exit)
+usage(int usage_exit)
{
const char *util = getnvmprogname();