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;