summaryrefslogtreecommitdiff
path: root/src/common/util.c
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2014-09-10 23:57:31 -0400
committerNick Mathewson <nickm@torproject.org>2014-09-10 23:57:31 -0400
commitd2463c0cfee066111c3a72d188cd897957aa2988 (patch)
treeaa7ae9404818663427882a61f59a03ab6434ba55 /src/common/util.c
parent3c2c6a61163cd6a42cc0eeee9fc43200b9f08503 (diff)
downloadtor-d2463c0cfee066111c3a72d188cd897957aa2988.tar.gz
tor-d2463c0cfee066111c3a72d188cd897957aa2988.zip
Avoid overflows and underflows in sscanf and friends
(Patch from teor on 13104)
Diffstat (limited to 'src/common/util.c')
-rw-r--r--src/common/util.c23
1 files changed, 17 insertions, 6 deletions
diff --git a/src/common/util.c b/src/common/util.c
index 2d7893b38a..297ae7806e 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -2804,10 +2804,14 @@ scan_unsigned(const char **bufp, unsigned long *out, int width, int base)
while (**bufp && (hex?TOR_ISXDIGIT(**bufp):TOR_ISDIGIT(**bufp))
&& scanned_so_far < width) {
int digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++);
- unsigned long new_result = result * base + digit;
- if (new_result < result)
- return -1; /* over/underflow. */
- result = new_result;
+ // Check for overflow beforehand, without actually causing any overflow
+ // This preserves functionality on compilers that don't wrap overflow
+ // (i.e. that trap or optimise away overflow)
+ // result * base + digit > ULONG_MAX
+ // result * base > ULONG_MAX - digit
+ if (result > (ULONG_MAX - digit)/base)
+ return -1; /* Processing this digit would overflow */
+ result = result * base + digit;
++scanned_so_far;
}
@@ -2842,10 +2846,17 @@ scan_signed(const char **bufp, long *out, int width)
if (scan_unsigned(bufp, &result, width, 10) < 0)
return -1;
- if (neg) {
+ if (neg && result > 0) {
if (result > ((unsigned long)LONG_MAX) + 1)
return -1; /* Underflow */
- *out = -(long)result;
+ // Avoid overflow on the cast to signed long when result is LONG_MIN
+ // by subtracting 1 from the unsigned long positive value,
+ // then, after it has been cast to signed and negated,
+ // subtracting the original 1 (the double-subtraction is intentional).
+ // Otherwise, the cast to signed could cause a temporary long
+ // to equal LONG_MAX + 1, which is undefined.
+ // We avoid underflow on the subtraction by treating -0 as positive.
+ *out = (-(long)(result - 1)) - 1;
} else {
if (result > LONG_MAX)
return -1; /* Overflow */