diff options
author | Nick Mathewson <nickm@torproject.org> | 2014-09-10 23:57:31 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2014-09-10 23:57:31 -0400 |
commit | d2463c0cfee066111c3a72d188cd897957aa2988 (patch) | |
tree | aa7ae9404818663427882a61f59a03ab6434ba55 /src/common/util.c | |
parent | 3c2c6a61163cd6a42cc0eeee9fc43200b9f08503 (diff) | |
download | tor-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.c | 23 |
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 */ |