diff options
| author | Leah Rowe <leah@libreboot.org> | 2026-03-20 04:02:51 +0000 |
|---|---|---|
| committer | Leah Rowe <leah@libreboot.org> | 2026-03-22 13:50:44 +0000 |
| commit | 6838db4647b600bf5b356429f54850bf801e7ba4 (patch) | |
| tree | cc98541897703d2949af27dc050cad8cba5061a0 /util/nvmutil/lib/num.c | |
| parent | f50ffd6bb13c04cb185fb6311f8875582bf18388 (diff) | |
WIP: hardened mktemp
i'm pretty much nearly there. still no dir support,
only files.
i won't keep amending now - will do more, then
squash later.
Signed-off-by: Leah Rowe <leah@libreboot.org>
Diffstat (limited to 'util/nvmutil/lib/num.c')
| -rw-r--r-- | util/nvmutil/lib/num.c | 235 |
1 files changed, 165 insertions, 70 deletions
diff --git a/util/nvmutil/lib/num.c b/util/nvmutil/lib/num.c index bbb5a83e..343350b0 100644 --- a/util/nvmutil/lib/num.c +++ b/util/nvmutil/lib/num.c @@ -4,6 +4,10 @@ * Numerical functions. */ +/* +TODO: properly handle errno in this file + */ + #ifdef __OpenBSD__ #include <sys/param.h> #endif @@ -30,31 +34,108 @@ #include "../include/common.h" +/* TODO: + * make this and errno handling more + * flexible + + in particular: + hextonum could be modified to + write into a buffer instead, + with the converted numbers, + of an arbitrary length + */ unsigned short hextonum(char ch_s) { + int saved_errno = errno; + + /* rlong() can return error, + but preserves errno if no + error. we need to detect + this because it handles + /dev/urandom sometimes + + therefore, if it's zero + at start, we know if there + was an err at the end, by + return value zero, if errno + was set; this is technically + valid, since zero is also + a valid random number! + + it's an edge case that i had + to fix. i'll rewrite the code + better later. for now, it + should be ok. + */ + errno = 0; + unsigned char ch; + size_t rval; ch = (unsigned char)ch_s; - if ((unsigned int)(ch - '0') <= 9) - return ch - '0'; + if ((unsigned int)(ch - '0') <= 9) { + + rval = ch - '0'; + goto hextonum_success; + } ch |= 0x20; - if ((unsigned int)(ch - 'a') <= 5) - return ch - 'a' + 10; + if ((unsigned int)(ch - 'a') <= 5) { + + rval = ch - 'a' + 10; + goto hextonum_success; + } - if (ch == '?' || ch == 'x') - return (unsigned short)rlong() & 0xf; + if (ch == '?' || ch == 'x') { + + rval = rlong(); + if (errno > 0) + goto err_hextonum; + + goto hextonum_success; + } + + goto err_hextonum; + +hextonum_success: + + errno = saved_errno; + return (unsigned short)rval & 0xf; + +err_hextonum: + + if (errno == saved_errno) + errno = EINVAL; + else + return 17; /* 17 indicates getrandom/urandom fail */ return 16; /* invalid character */ + + /* caller just checks >15. */ } /* Random numbers */ -unsigned long +/* when calling this: save errno + * first, then set errno to zero. + * on error, this function will + * set errno and possibly return + * + * rlong also preserves errno + * and leaves it unchanged on + * success, so if you do it + * right, you can detect error. + * this is because it uses + * /dev/urandom which can err. + * ditto getrandom (EINTR), + * theoretically. + */ + +size_t rlong(void) { #if !(defined(FALLBACK_RAND_1989) && \ @@ -63,14 +144,17 @@ rlong(void) defined(__FreeBSD__) || \ defined(__NetBSD__) || defined(__APPLE__) - unsigned long rval; - arc4random_buf(&rval, sizeof(unsigned long)); + int saved_errno = errno; + size_t rval; + arc4random_buf(&rval, sizeof(size_t)); + + errno = saved_errno; return rval; #else static int fd = -1; - static long nr = -1; - static unsigned long off = 0; + static ssize_t nr = -1; + static size_t off = 0; #if defined (BUFSIZ) static char rbuf[BUFSIZ]; #else @@ -82,12 +166,14 @@ rlong(void) static char rbuf[4096]; /* typical 32-bit BUFSIZ */ #endif #endif - unsigned long rval; - long new_nr; + size_t rval; + ssize_t new_nr; int retries = 0; int max_retries = 100; + int saved_errno = errno; + #if defined(__linux__) #if defined(HAVE_GETRANDOM) || \ defined(HAVE_GETRANDOM_SYSCALL) @@ -98,17 +184,12 @@ rlong(void) * modern linux, but not on * every libc. better use the * official linux function - * - * similar benefits to arc4random - * e.g. works in chroot, blocks - * until it has enough entropy, - * and works even when /dev/urandom - * is available (doesn't use it); - * it's generally more reliable */ - if (fallback_rand_getrandom(&rval, sizeof(rval)) == 0) + if (fallback_rand_getrandom(&rval, sizeof(rval)) == 0) { + errno = saved_errno; return rval; + } /* * now fall back to urandom if getrandom failed: @@ -131,15 +212,15 @@ rlong(void) retry_urandom_read: if (++retries > max_retries) - goto rlong_next; + goto err_rlong; - if (nr < 0 || nr < (long)sizeof(unsigned long)) { + if (nr < 0 || nr < (ssize_t)sizeof(size_t)) { if (fd < 0) { fd = open("/dev/urandom", O_RDONLY | O_BINARY | O_NOFOLLOW | - O_CLOEXEC); + O_CLOEXEC | O_NOCTTY); #ifdef USE_OLD_DEV_RANDOM #if (USE_OLD_DEV_RANDOM) > 0 @@ -155,7 +236,7 @@ retry_urandom_read: if (fd < 0) fd = open("/dev/random", O_RDONLY | O_BINARY | O_NOFOLLOW | - O_CLOEXEC); + O_CLOEXEC | O_NOCTTY); #endif #endif @@ -169,7 +250,7 @@ retry_urandom_read: sizeof(rbuf), 0, IO_READ, LOOP_EAGAIN, LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR); - if (new_nr < 0 || new_nr < (long)sizeof(rbuf)) + if (new_nr < 0 || new_nr < (ssize_t)sizeof(rbuf)) goto retry_urandom_read; /* only reset buffer after successful refill */ @@ -187,37 +268,37 @@ retry_urandom_read: fd = -1; retries = 0; - memcpy(&rval, rbuf + off, sizeof(unsigned long)); + memcpy(&rval, rbuf + off, sizeof(size_t)); - nr -= (long)sizeof(unsigned long); - off += sizeof(unsigned long); + nr -= (ssize_t)sizeof(size_t); + off += sizeof(size_t); + + errno = saved_errno; return rval; -rlong_next: +err_rlong: - fd = -1; - off = 0; - nr = -1; + if (errno == saved_errno) + errno = EIO; - err(EIO, "Can't read from /dev/[ua]random"); return 0; #endif #else /* FALLBACK_RAND_1989 */ /* your computer is from a museum */ - unsigned long mix = 0; - int nr; + size_t mix = 0; + int nr = 0; + int saved_errno = errno; /* 100 times, for entropy */ for (nr = 0; nr < 100; nr++) mix ^= fallback_rand_1989(); - /* 101 times ;) - */ - return fallback_rand_1989(); + errno = saved_errno; + return mix; #endif } @@ -226,17 +307,22 @@ rlong_next: #if defined(__linux__) #if defined(HAVE_GETRANDOM) || \ defined(HAVE_GETRANDOM_SYSCALL) -int -fallback_rand_getrandom(void *buf, unsigned long len) +int /* yes, linux is a fallback */ +fallback_rand_getrandom(void *buf, size_t len) { - unsigned long off = 0; - long rval = -1; + size_t off = 0; + ssize_t rval = -1; + int saved_errno = errno; - if (!len) + if (!len) { + errno = EINVAL; return -1; + } - if (buf == NULL) + if (buf == NULL) { + errno = EFAULT; return -1; + } #if defined(HAVE_GETRANDOM) || \ defined(HAVE_GETRANDOM_SYSCALL) @@ -244,51 +330,60 @@ fallback_rand_getrandom(void *buf, unsigned long len) while (off < len) { #if defined(HAVE_GETRANDOM) - rval = (long)getrandom((char *)buf + off, len - off, 0); + rval = (ssize_t)getrandom((char *)buf + off, len - off, 0); #elif defined(HAVE_GETRANDOM_SYSCALL) - rval = (long)syscall(SYS_getrandom, + rval = (ssize_t)syscall(SYS_getrandom, (char *)buf + off, len - off, 0); #endif if (rval < 0) { - if (errno == EINTR) + if (errno == EINTR || errno == EAGAIN) continue; + errno = EIO; return -1; /* unsupported by kernel */ } - off += (unsigned long)rval; + if (rval == 0) { + errno = EIO; + return -1; + } + + off += (size_t)rval; } + errno = saved_errno; return 0; #else (void)buf; (void)len; + errno = EIO; return -1; #endif } #endif #endif #else -/* nobody should use this - * (not crypto-safe) - */ -unsigned long +size_t fallback_rand_1989(void) { - static unsigned long mix = 0; - static unsigned long counter = 0; + static size_t mix = 0; + static size_t counter = 0; struct timeval tv; + /* nobody should use this + * (not crypto-safe) + */ + gettimeofday(&tv, NULL); - mix ^= (unsigned long)tv.tv_sec - ^ (unsigned long)tv.tv_usec - ^ (unsigned long)getpid() - ^ (unsigned long)&mix + mix ^= (size_t)tv.tv_sec + ^ (size_t)tv.tv_usec + ^ (size_t)getpid() + ^ (size_t)&mix ^ counter++ ^ entropy_jitter(); @@ -296,20 +391,20 @@ fallback_rand_1989(void) * Stack addresses can vary between * calls, thus increasing entropy. */ - mix ^= (unsigned long)&mix; - mix ^= (unsigned long)&tv; - mix ^= (unsigned long)&counter; + mix ^= (size_t)&mix; + mix ^= (size_t)&tv; + mix ^= (size_t)&counter; return mix; } -unsigned long +size_t entropy_jitter(void) { - unsigned long mix; + size_t mix; struct timeval a, b; - long mix_diff; + ssize_t mix_diff; int c; @@ -326,13 +421,13 @@ entropy_jitter(void) * prevent negative numbers to prevent overflow, * which would bias rand to large numbers */ - mix_diff = (long)(b.tv_usec - a.tv_usec); + mix_diff = (ssize_t)(b.tv_usec - a.tv_usec); if (mix_diff < 0) mix_diff = -mix_diff; - mix ^= (unsigned long)(mix_diff); + mix ^= (size_t)(mix_diff); - mix ^= (unsigned long)&mix; + mix ^= (size_t)&mix; } @@ -341,9 +436,9 @@ entropy_jitter(void) #endif void -check_bin(unsigned long a, const char *a_name) +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, (size_t)a); } |
