X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=Usermode%2FLibraries%2Flibc.so_src%2Fstrtoi.c;h=84bf0005919edbacef6a2dc0967f07fffe40d88c;hb=92c5980925e773c6e1d6775c50c9d86c77b84d23;hp=758906fd3bf906b901d32b0c4713e961b189c150;hpb=8cf9dc88c488ba959a211f1ec653a366d16e1531;p=tpg%2Facess2.git diff --git a/Usermode/Libraries/libc.so_src/strtoi.c b/Usermode/Libraries/libc.so_src/strtoi.c index 758906fd..84bf0005 100644 --- a/Usermode/Libraries/libc.so_src/strtoi.c +++ b/Usermode/Libraries/libc.so_src/strtoi.c @@ -12,7 +12,7 @@ unsigned long long strtoull(const char *str, char **end, int base) { - long long ret = 0; + unsigned long long ret = 0; if( !str || base < 0 || base > 36 || base == 1 ) { if(end) @@ -27,7 +27,7 @@ unsigned long long strtoull(const char *str, char **end, int base) // Handle base detection for hex if( base == 0 || base == 16 ) { - if( *str == '0' && str[1] == 'x' ) { + if( *str == '0' && (str[1] == 'x' || str[1] == 'X') && isxdigit(str[2]) ) { str += 2; base = 16; } @@ -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; @@ -54,12 +58,35 @@ unsigned long long strtoull(const char *str, char **end, int base) if( '0' <= *str && *str <= '9' ) next = *str - '0'; if( 'A' <= *str && *str <= 'A'+base-10-1 ) - next = *str - 'A'; + next = *str - 'A' + 10; if( 'a' <= *str && *str <= 'a'+base-10-1 ) - next = *str - 'a'; + 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; + } + + // 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; str ++; @@ -85,7 +112,6 @@ unsigned long strtoul(const char *ptr, char **end, int base) long long strtoll(const char *str, char **end, int base) { int neg = 0; - unsigned long long ret; if( !str ) { errno = EINVAL; @@ -96,17 +122,37 @@ long long strtoll(const char *str, char **end, int base) str++; // Check for negative (or positive) sign - if(*str == '-' || *str == '+') { + if(*str == '-' || *str == '+') + { + //_SysDebug("strtoll - str[0:1] = '%.2s'", str); + if( !isdigit(str[1]) ) { + // Non-digit, invalid string + if(end) *end = (char*)str; + return 0; + } neg = (*str == '-'); str++; } - ret = strtoull(str, end, base); + unsigned long long ret = strtoull(str, end, base); + //_SysDebug("strtoll - neg=%i,ret=%llu", neg, ret); - if( neg ) + if( neg ) { + // Abuses unsigned integer overflow + if( ret + LLONG_MIN < ret ) { + errno = ERANGE; + return LLONG_MIN; + } return -ret; + } else + { + if( ret > LLONG_MAX ) { + errno = ERANGE; + return LLONG_MAX; + } return ret; + } } long strtol(const char *str, char **end, int base)