summaryrefslogtreecommitdiff
path: root/util/nvmutil/lib/num.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/nvmutil/lib/num.c')
-rw-r--r--util/nvmutil/lib/num.c235
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);
}