From 0b3db65a6c3babf70808360b455f9b9e1886655f Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 10 May 2014 13:11:44 +0800 Subject: [PATCH] Usermode/libc - Fix and unit test strtoull/* behavior (edge cases) --- Usermode/Libraries/Makefile.tpl | 4 +- Usermode/Libraries/libc.so_src/TEST_strtoi.c | 64 ++++++++++++++++++-- Usermode/Libraries/libc.so_src/strtoi.c | 43 +++++++++++-- 3 files changed, 99 insertions(+), 12 deletions(-) diff --git a/Usermode/Libraries/Makefile.tpl b/Usermode/Libraries/Makefile.tpl index a99ff144..5317079e 100644 --- a/Usermode/Libraries/Makefile.tpl +++ b/Usermode/Libraries/Makefile.tpl @@ -115,10 +115,10 @@ $(OUTPUTDIR)Libs/%: obj-native/%.no: %.c @mkdir -p $(dir $@) - $(NCC) -c $< -o $@ -Wall -std=gnu99 -MD -MP -MF $@.dep + $(NCC) -g -c $< -o $@ -Wall -std=gnu99 -MD -MP -MF $@.dep '-D_SysDebug(f,v...)=fprintf(stderr,"DEBUG "f"\n",##v)' -include stdio.h TEST_%: obj-native/TEST_%.no obj-native/%.no - $(NCC) -o $@ $^ + $(NCC) -g -o $@ $^ -include $(UTESTS:%=obj-native/TEST_%.no.dep) -include $(UTESTS:%=obj-native/%.no.dep) diff --git a/Usermode/Libraries/libc.so_src/TEST_strtoi.c b/Usermode/Libraries/libc.so_src/TEST_strtoi.c index 6d89224e..62f2e695 100644 --- a/Usermode/Libraries/libc.so_src/TEST_strtoi.c +++ b/Usermode/Libraries/libc.so_src/TEST_strtoi.c @@ -7,15 +7,69 @@ */ #include #include +#include +#include +#include // strerror -#define TST(t, class, base, val, exp, fmt) do {\ - t ret = strto##class(#val, NULL, base); \ +#define STR_(v) #v +#define STR(v) STR_(v) +#define TST(t, class, base, val, exp, fmt, ofs, exp_errno) do {\ + const char *in = val;\ + char *end;\ + errno = 0;\ + t ret = strto##class(in, &end, base); \ if( ret != exp ) \ - printf("FAIL strto"#class"('"#val"') != "#val" (act 0x"fmt")\n", ret);\ + fprintf(stderr, "FAIL strto"#class"('%s') != "#exp" (act "fmt")\n", in, ret);\ + if( end != in+ofs ) \ + fprintf(stderr, "FAIL strto"#class"('%s') returned wrong end: %p (+%zi) instead of %p (+%zi)\n",\ + in,end,end-in,in+ofs,(size_t)ofs);\ + if( exp_errno != errno ) \ + fprintf(stderr, "FAIL strto"#class"('%s') returned wrong errno, exp '%s', got '%s'\n",\ + in, strerror(exp_errno), strerror(errno));\ }while(0) +#define PRIMEBUF(fmt, val) buf_len = snprintf(buf, sizeof(buf), fmt, val) + int main(int argc, char *argv[]) { - TST(unsigned long, ul, 0, 0x10ec, 0x10ec, "%lx"); - TST(unsigned long long, ull, 0, 0xffeed10ec, 0xffeed10ec, "%llx"); + char buf[64]; + size_t buf_len; + + // Success cases + TST(unsigned long, ul, 0, "0x10ec", 0x10ec, "%lx", 2+4, 0); + TST(unsigned long long, ull, 0, "0xffeed10ec", 0xffeed10ec, "%llx", 2+9, 0); + 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 + + // Invalid strings + TST(unsigned long long, ull, 0, "0x", 0, "%llx", 1, 0); // Single 0 + TST(unsigned long long, ull, 0, "0xg", 0, "%llx", 1, 0); // Single 0 + TST(unsigned long long, ull, 0, "-a", 0, "%llx", 0, 0); // Nothing + TST(long long, ll, 0, "-a", 0, "%lld", 0, 0); // Nothing + TST(long long, ll, 0, "-1aaatg", -1, "%lld", 2, 0); // -1 (with traling junk) + TST(long long, ll, 0, "-+1aaatg", 0, "%lld", 0, 0); // Nothing + TST(long long, ll, 0, "- 1", 0, "%lld", 0, 0); // Nothing + TST(long long, ll, 0, "01278 1", 0127, "%lld", 4, 0); // 0127 with junk + + // Range edges + PRIMEBUF("0x%llx", ULLONG_MAX); + TST(unsigned long long, ull, 0, buf, ULLONG_MAX, "0x%llx", buf_len, 0); + PRIMEBUF("%llu", ULLONG_MAX); + TST(unsigned long long, ull, 0, buf, ULLONG_MAX, "%llu", buf_len, 0); + PRIMEBUF("-%llu", (long long)LONG_MAX); + TST(long, l, 0, buf, -LONG_MAX, "%ld", buf_len, 0); + + // Out of range + // - When the range limit is hit, valid characters should still be consumed (just not used) + TST(unsigned long long, ull, 0, "0x10000FFFF0000FFFF", ULLONG_MAX, "%llx", strlen(in), ERANGE); + TST(unsigned long, ul, 0, "0x10000FFFF0000FFFF", ULONG_MAX, "%lx", strlen(in), ERANGE); + TST(long, l, 0, "0x10000FFFF0000FFFF", LONG_MAX, "%ld", strlen(in), ERANGE); + TST(long, l, 0, "-0x10000FFFF0000FFFF", LONG_MIN, "%ld", strlen(in), ERANGE); + if( LONG_MIN < -LONG_MAX ) + { + // Ensure that if -LONG_MIN is greater than LONG_MAX, that converting it leaves a range error + PRIMEBUF("%ld", LONG_MIN); + TST(long, l, 0, buf+1, LONG_MAX, "%ld", buf_len-1, ERANGE); + } } diff --git a/Usermode/Libraries/libc.so_src/strtoi.c b/Usermode/Libraries/libc.so_src/strtoi.c index de7f7254..db6684f0 100644 --- a/Usermode/Libraries/libc.so_src/strtoi.c +++ b/Usermode/Libraries/libc.so_src/strtoi.c @@ -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; } @@ -60,6 +60,21 @@ unsigned long long strtoull(const char *str, char **end, int base) } if( next < 0 ) break; + + 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; + } ret *= base; ret += next; str ++; @@ -85,7 +100,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 +110,36 @@ 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 ) { + if( -ret < LLONG_MIN ) { + 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) -- 2.20.1