From: Tim Kientzle Date: Sat, 7 Dec 2013 22:51:26 +0000 (-0800) Subject: Issue 320: Rewrite (again) to avoid the left shift that CLang dislikes so much. X-Git-Tag: v3.1.900a~361^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6cf33c9307bbf5827df9500750e59a35b6f2d5d3;p=thirdparty%2Flibarchive.git Issue 320: Rewrite (again) to avoid the left shift that CLang dislikes so much. This version simulates 2s-complement arithmetic on an unsigned value, then casts it to signed at the end. --- diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c index 95960a2c1..c5b8fd99d 100644 --- a/libarchive/archive_read_support_format_tar.c +++ b/libarchive/archive_read_support_format_tar.c @@ -2414,9 +2414,10 @@ tar_atol(const char *p, size_t char_cnt) static int64_t tar_atol_base_n(const char *p, size_t char_cnt, int base) { - int64_t l, limit, last_digit_limit; + int64_t l, maxval, limit, last_digit_limit; int digit, sign; + maxval = INT64_MAX; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; @@ -2433,6 +2434,10 @@ tar_atol_base_n(const char *p, size_t char_cnt, int base) sign = -1; p++; char_cnt--; + + maxval = INT64_MIN; + limit = -(INT64_MIN / base); + last_digit_limit = INT64_MIN % base; } l = 0; @@ -2440,8 +2445,7 @@ tar_atol_base_n(const char *p, size_t char_cnt, int base) digit = *p - '0'; while (digit >= 0 && digit < base && char_cnt != 0) { if (l>limit || (l == limit && digit > last_digit_limit)) { - l = INT64_MAX; /* Truncate on overflow. */ - break; + return maxval; /* Truncate on overflow. */ } l = (l * base) + digit; digit = *++p - '0'; @@ -2464,32 +2468,56 @@ tar_atol10(const char *p, size_t char_cnt) } /* - * Parse a base-256 integer. This is just a straight signed binary - * value in big-endian order, except that the high-order bit is - * ignored. + * Parse a base-256 integer. This is just a variable-length + * twos-complement signed binary value in big-endian order, except + * that the high-order bit is ignored. The values here can be up to + * 12 bytes, so we need to be careful about overflowing 64-bit + * (8-byte) integers. + * + * This code unashamedly assumes that the local machine uses 8-bit + * bytes and twos-complement arithmetic. */ static int64_t tar_atol256(const char *_p, size_t char_cnt) { - int64_t l, upper_limit, lower_limit; - const unsigned char *p = (const unsigned char *)_p; + uint64_t l; + const unsigned char *p = (const unsigned char *)_p; + unsigned char c, neg; + + /* Extend 7-bit 2s-comp to 8-bit 2s-comp, decide sign. */ + c = *p; + if (c & 0x40) { + neg = 0xff; + c |= 0x80; + l = ~0ULL; + } else { + neg = 0; + c &= 0x7f; + l = 0; + } - upper_limit = INT64_MAX / 256; - lower_limit = INT64_MIN / 256; + /* If more than 8 bytes, check that we can ignore + * high-order bits without overflow. */ + while (char_cnt > sizeof(int64_t)) { + --char_cnt; + if (c != neg) + return neg ? INT64_MIN : INT64_MAX; + c = *++p; + } - /* Sign-extend the 7-bit value to 64 bits. */ - if ((0x40 & *p) == 0x40) - l = ~((int64_t)0x3f) | *p++; - else - l = 0x3f & *p++; - while (--char_cnt > 0) { - if (l > upper_limit) - return (INT64_MAX); /* Truncate on overflow */ - else if (l < lower_limit) - return (INT64_MIN); - l = (l << 8) | (0xff & (int64_t)*p++); - } - return (l); + /* c is first byte that fits; if sign mismatch, return overflow */ + if ((c ^ neg) & 0x80) { + return neg ? INT64_MIN : INT64_MAX; + } + + /* Accumulate remaining bytes. */ + while (--char_cnt > 0) { + l = (l << 8) | c; + c = *++p; + } + l = (l << 8) | c; + /* Return signed twos-complement value. */ + return (int64_t)(l); } /*