utils: fix range checking for get_u32/get_u64 et all
Be more careful about overflow in strtoXX routines.
Checks are based on documented interface on man pages.
Based on suggestion from "Mr Dash Four".
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
diff --git a/lib/utils.c b/lib/utils.c
index 5bcdbcf..bcd6002 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -12,6 +12,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <math.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
@@ -38,9 +39,28 @@
if (!arg || !*arg)
return -1;
+
res = strtol(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN)
+
+ /* If there were no digits at all, strtol() stores
+ * the original value of nptr in *endptr (and returns 0).
+ * In particular, if *nptr is not '\0' but **endptr is '\0' on return,
+ * the entire string is valid.
+ */
+ if (!ptr || ptr == arg || *ptr)
return -1;
+
+ /* If an underflow occurs, strtol() returns LONG_MIN.
+ * If an overflow occurs, strtol() returns LONG_MAX.
+ * In both cases, errno is set to ERANGE.
+ */
+ if ((res == LONG_MAX || res == LONG_MIN) && errno == ERANGE)
+ return -1;
+
+ /* Outside range of int */
+ if (res < INT_MIN || res > INT_MAX)
+ return -1;
+
*val = res;
return 0;
}
@@ -87,9 +107,21 @@
if (!arg || !*arg)
return -1;
+
res = strtoul(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > UINT_MAX)
+
+ /* empty string or trailing non-digits */
+ if (!ptr || ptr == arg || *ptr)
return -1;
+
+ /* overflow */
+ if (res == ULONG_MAX && errno == ERANGE)
+ return -1;
+
+ /* out side range of unsigned */
+ if (res > UINT_MAX)
+ return -1;
+
*val = res;
return 0;
}
@@ -107,17 +139,32 @@
unsigned long res;
char *p;
- if (strchr(arg,'.') != NULL) {
- t = strtod(arg,&p);
+ if (strchr(arg, '.') != NULL) {
+ t = strtod(arg, &p);
if (t < 0.0)
return -1;
- }
- else {
- res = strtoul(arg, &p, 0);
- if (res > UINT_MAX)
+
+ /* extra non-digits */
+ if (!p || p == arg || *p)
return -1;
+
+ /* over/underflow */
+ if ((t == HUGE_VALF || t == HUGE_VALL) && errno == ERANGE)
+ return -1;
+ } else {
+ res = strtoul(arg, &p, 0);
+
+ /* empty string or trailing non-digits */
+ if (!p || p == arg || *p)
+ return -1;
+
+ /* overflow */
+ if (res == ULONG_MAX && errno == ERANGE)
+ return -1;
+
t = (double)res;
}
+
if (p == arg)
return -1;
*raw = 1;
@@ -151,9 +198,21 @@
if (!arg || !*arg)
return -1;
+
res = strtoull(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res == 0xFFFFFFFFULL)
- return -1;
+
+ /* empty string or trailing non-digits */
+ if (!ptr || ptr == arg || *ptr)
+ return -1;
+
+ /* overflow */
+ if (res == ULLONG_MAX && errno == ERANGE)
+ return -1;
+
+ /* in case ULL is 128 bits */
+ if (res > 0xFFFFFFFFFFFFFFFFULL)
+ return -1;
+
*val = res;
return 0;
}
@@ -166,8 +225,19 @@
if (!arg || !*arg)
return -1;
res = strtoul(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
+
+ /* empty string or trailing non-digits */
+ if (!ptr || ptr == arg || *ptr)
return -1;
+
+ /* overflow */
+ if (res == ULONG_MAX && errno == ERANGE)
+ return -1;
+
+ /* in case UL > 32 bits */
+ if (res > 0xFFFFFFFFUL)
+ return -1;
+
*val = res;
return 0;
}
@@ -180,8 +250,18 @@
if (!arg || !*arg)
return -1;
res = strtoul(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > 0xFFFF)
+
+ /* empty string or trailing non-digits */
+ if (!ptr || ptr == arg || *ptr)
return -1;
+
+ /* overflow */
+ if (res == ULONG_MAX && errno == ERANGE)
+ return -1;
+
+ if (res > 0xFFFFUL)
+ return -1;
+
*val = res;
return 0;
}
@@ -193,9 +273,19 @@
if (!arg || !*arg)
return -1;
+
res = strtoul(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > 0xFF)
+ /* empty string or trailing non-digits */
+ if (!ptr || ptr == arg || *ptr)
return -1;
+
+ /* overflow */
+ if (res == ULONG_MAX && errno == ERANGE)
+ return -1;
+
+ if (res > 0xFFUL)
+ return -1;
+
*val = res;
return 0;
}
@@ -210,10 +300,13 @@
if (!arg || !*arg)
return -1;
res = strtol(arg, &ptr, base);
- if (ptr == arg || *ptr ||
- ((res == LONG_MIN || res == LONG_MAX) && errno == ERANGE) ||
- res > INT32_MAX || res < INT32_MIN)
+ if (!ptr || ptr == arg || *ptr)
return -1;
+ if ((res == LONG_MIN || res == LONG_MAX) && errno == ERANGE)
+ return -1;
+ if (res > INT32_MAX || res < INT32_MIN)
+ return -1;
+
*val = res;
return 0;
}
@@ -226,8 +319,13 @@
if (!arg || !*arg)
return -1;
res = strtol(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000)
+ if (!ptr || ptr == arg || *ptr)
return -1;
+ if ((res == LONG_MIN || res == LONG_MAX) && errno == ERANGE)
+ return -1;
+ if (res > 0x7FFF || res < -0x8000)
+ return -1;
+
*val = res;
return 0;
}
@@ -240,7 +338,11 @@
if (!arg || !*arg)
return -1;
res = strtol(arg, &ptr, base);
- if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80)
+ if (!ptr || ptr == arg || *ptr)
+ return -1;
+ if ((res == LONG_MIN || res == LONG_MAX) && errno == ERANGE)
+ return -1;
+ if (res > 0x7F || res < -0x80)
return -1;
*val = res;
return 0;