Use a much less clever scan_signed no-overflow hack

This commit is contained in:
Nick Mathewson 2016-05-12 14:33:26 -04:00
parent a7207329a8
commit 6bc052365a

View File

@ -3060,7 +3060,7 @@ digit_to_num(char d)
* success, store the result in <b>out</b>, advance bufp to the next * success, store the result in <b>out</b>, advance bufp to the next
* character, and return 0. On failure, return -1. */ * character, and return 0. On failure, return -1. */
static int 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; unsigned long result = 0;
int scanned_so_far = 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)) while (**bufp && (hex?TOR_ISXDIGIT(**bufp):TOR_ISDIGIT(**bufp))
&& scanned_so_far < width) { && 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 // Check for overflow beforehand, without actually causing any overflow
// This preserves functionality on compilers that don't wrap overflow // This preserves functionality on compilers that don't wrap overflow
// (i.e. that trap or optimise away 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 (neg && result > 0) {
if (result > ((unsigned long)LONG_MAX) + 1) if (result > ((unsigned long)LONG_MAX) + 1)
return -1; /* Underflow */ return -1; /* Underflow */
// Avoid overflow on the cast to signed long when result is LONG_MIN else if (result == ((unsigned long)LONG_MAX) + 1)
// by subtracting 1 from the unsigned long positive value, *out = LONG_MIN;
// then, after it has been cast to signed and negated, else {
// subtracting the original 1 (the double-subtraction is intentional). /* We once had a far more clever no-overflow conversion here, but
// Otherwise, the cast to signed could cause a temporary long * some versions of GCC apparently ran it into the ground. Now
// to equal LONG_MAX + 1, which is undefined. * we just check for LONG_MIN explicitly.
// We avoid underflow on the subtraction by treating -0 as positive. */
*out = (-(long)(result - 1)) - 1; *out = -(long)result;
}
} else { } else {
if (result > LONG_MAX) if (result > LONG_MAX)
return -1; /* Overflow */ return -1; /* Overflow */