From e9ff6475019f9df9c1603483f1c090c24a61913b Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 10 May 2014 15:38:58 +0800 Subject: [PATCH] Usermode/libc - Fix over-use of 64-bit divide in strtoull --- Usermode/Libraries/libc.so_src/TEST_strtoi.c | 2 ++ Usermode/Libraries/libc.so_src/strtoi.c | 26 ++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/Usermode/Libraries/libc.so_src/TEST_strtoi.c b/Usermode/Libraries/libc.so_src/TEST_strtoi.c index 62f2e695..e0658087 100644 --- a/Usermode/Libraries/libc.so_src/TEST_strtoi.c +++ b/Usermode/Libraries/libc.so_src/TEST_strtoi.c @@ -41,6 +41,8 @@ int main(int argc, char *argv[]) TST(unsigned long long, ull, 0, "01234567", 01234567, "%llo", 8, 0); TST(unsigned long long, ull, 0, "1234567", 1234567, "%lld", 7, 0); TST(long long, ll, 0, "-1", -1, "%lld", 2, 0); // -1 + TST(long long, ll, 0, "100113", 100113, "%lld", strlen(in), 0); + TST(long long, ll, 0, "0x101", 0x101, "0x%llx", strlen(in), 0); // Invalid strings TST(unsigned long long, ull, 0, "0x", 0, "%llx", 1, 0); // Single 0 diff --git a/Usermode/Libraries/libc.so_src/strtoi.c b/Usermode/Libraries/libc.so_src/strtoi.c index db6684f0..4fd2da5b 100644 --- a/Usermode/Libraries/libc.so_src/strtoi.c +++ b/Usermode/Libraries/libc.so_src/strtoi.c @@ -43,6 +43,10 @@ unsigned long long strtoull(const char *str, char **end, int base) if( base == 0 ) base = 10; + // Value before getting within 1 digit of ULLONG_MAX + // - Used to avoid overflow in more accurate check + unsigned long long max_before_ullong_max = ULLONG_MAX / base; + unsigned int space_above = ULLONG_MAX - max_before_ullong_max * base; while( *str ) { int next = -1; @@ -58,22 +62,30 @@ unsigned long long strtoull(const char *str, char **end, int base) if( 'a' <= *str && *str <= 'a'+base-10-1 ) next = *str - 'a' + 10; } + //_SysDebug("strtoull - ret=0x%llx,next=%i,str='%s'", ret, next, str); if( next < 0 ) break; + // If we're already out of range, keep eating if( ret == ULLONG_MAX ) { errno = ERANGE; str ++; // Keep eating until first unrecognised character continue; } - if( ret > (ULLONG_MAX-next)/base ) { - //_SysDebug("strtoull - Out of range (0x%llx > 0x%llx)", ret, (ULLONG_MAX-next)/base); - //_SysDebug("strtoull - (%llu > %llu)", ret, (ULLONG_MAX-next)/base); - ret = ULLONG_MAX; - errno = ERANGE; - str ++; - continue; + + // Rough then accurate check against max value + if( ret >= max_before_ullong_max ) + { + //_SysDebug("strtoull - 0x%llx>0x%llx", ret, max_before_ullong_max); + if( (ret - max_before_ullong_max) * base + next > space_above ) { + //_SysDebug("strtoull - %u*%u+%u (%u) > %u", + // (unsigned int)(ret - max_before_ullong_max), base, next, space_above); + ret = ULLONG_MAX; + errno = ERANGE; + str ++; + continue; + } } ret *= base; ret += next; -- 2.20.1