diff options
author | Nick Mathewson <nickm@torproject.org> | 2014-09-11 00:11:26 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2014-09-11 00:11:26 -0400 |
commit | 48558ed1aa070ef121b339eef88e7822f8e45978 (patch) | |
tree | d757235e3645d73e1ca0026afa4f8c6058ad0917 /src/common/util.c | |
parent | 73ee161d8a7bc06eefa58654cbfe421449921eec (diff) | |
parent | 2491eadf002e993bee11aa76597ec7f361d8f6e5 (diff) | |
download | tor-48558ed1aa070ef121b339eef88e7822f8e45978.tar.gz tor-48558ed1aa070ef121b339eef88e7822f8e45978.zip |
Merge remote-tracking branch 'public/bug13104_025'
Diffstat (limited to 'src/common/util.c')
-rw-r--r-- | src/common/util.c | 31 |
1 files changed, 24 insertions, 7 deletions
diff --git a/src/common/util.c b/src/common/util.c index 97cedd519d..f4d293c838 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2837,10 +2837,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; } @@ -2875,10 +2879,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 */ @@ -3577,7 +3588,13 @@ format_helper_exit_status(unsigned char child_state, int saved_errno, /* Convert errno to be unsigned for hex conversion */ if (saved_errno < 0) { - unsigned_errno = (unsigned int) -saved_errno; + // Avoid overflow on the cast to unsigned int when result is INT_MIN + // by adding 1 to the signed int negative value, + // then, after it has been negated and cast to unsigned, + // adding the original 1 back (the double-addition is intentional). + // Otherwise, the cast to signed could cause a temporary int + // to equal INT_MAX + 1, which is undefined. + unsigned_errno = ((unsigned int) -(saved_errno + 1)) + 1; } else { unsigned_errno = (unsigned int) saved_errno; } |