diff options
author | Nick Mathewson <nickm@torproject.org> | 2016-05-12 14:33:26 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2016-05-12 14:33:26 -0400 |
commit | 6bc052365a8dbfc9ed7c43e69761099ba973af74 (patch) | |
tree | db8ffd00312d9de4cddd05c3929542f2c2bbeceb /src/common | |
parent | a7207329a85a156e20f91434347e2741d4d90025 (diff) | |
download | tor-6bc052365a8dbfc9ed7c43e69761099ba973af74.tar.gz tor-6bc052365a8dbfc9ed7c43e69761099ba973af74.zip |
Use a much less clever scan_signed no-overflow hack
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/util.c | 21 |
1 files changed, 11 insertions, 10 deletions
diff --git a/src/common/util.c b/src/common/util.c index fa2953cc30..d290f545a0 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -3060,7 +3060,7 @@ digit_to_num(char d) * success, store the result in <b>out</b>, advance bufp to the next * character, and return 0. On failure, return -1. */ static int -scan_unsigned(const char **bufp, unsigned long *out, int width, int base) +scan_unsigned(const char **bufp, unsigned long *out, int width, unsigned base) { unsigned long result = 0; int scanned_so_far = 0; @@ -3073,7 +3073,7 @@ 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 digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++); // 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) @@ -3119,14 +3119,15 @@ scan_signed(const char **bufp, long *out, int width) if (neg && result > 0) { if (result > ((unsigned long)LONG_MAX) + 1) return -1; /* Underflow */ - // 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 == ((unsigned long)LONG_MAX) + 1) + *out = LONG_MIN; + else { + /* We once had a far more clever no-overflow conversion here, but + * some versions of GCC apparently ran it into the ground. Now + * we just check for LONG_MIN explicitly. + */ + *out = -(long)result; + } } else { if (result > LONG_MAX) return -1; /* Overflow */ |