From 06f975f75c4cb9f54c15f25c7dce35f3544c8301 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Thu, 20 May 2021 11:01:15 +0200 Subject: [PATCH] stdlib: add a test strtol, strtoul, strtoll (DONE) The presumed bug where the value computed without the sign overflows even though the negative result can be represented is not actually a problem, because this only happens with signed results and the temporary value is computed as unsigned (thus with extra range). --- CMakeLists.txt | 3 +++ STATUS | 3 +-- src/libc/stdlib/stdlib_p.h | 2 +- src/libc/stdlib/strto_int.c | 21 ++++++++++++++------- src/libc/stdlib/strtol.c | 10 ++++++++++ src/libc/stdlib/strtoll.c | 11 +++++++++++ src/libc/stdlib/strtoul.c | 11 +++++++++++ 7 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 src/libc/stdlib/strtol.c create mode 100644 src/libc/stdlib/strtoll.c create mode 100644 src/libc/stdlib/strtoul.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 4895a50..79f43ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,6 +107,9 @@ set(SOURCES src/libc/stdlib/lldiv.c src/libc/stdlib/reallocarray.c src/libc/stdlib/strto_int.c + src/libc/stdlib/strtol.c + src/libc/stdlib/strtoll.c + src/libc/stdlib/strtoul.c src/libc/stdlib/strtoull.c # string src/libc/string/strchr.c diff --git a/STATUS b/STATUS index 148bbbd..a7f1d24 100644 --- a/STATUS +++ b/STATUS @@ -100,8 +100,7 @@ DONE: Function/symbol/macro is defined, builds, links, and is tested 7.20.1.1 atof: TODO 7.20.1.2 atoi, atol, atoll: TODO 7.20.1.3 strtod, strtof, strtold: TODO - 7.20.1.4 strtol, strtoul, strtoll: TODO - strtoull: DONE + 7.20.1.4 strtol, strtoul, strtoll, strtoull: DONE 7.20.2 Pseudo-random sequence generation functions: TODO 7.20.3 Memory management functions: TODO (check existing code first) 7.20.4 Communication with the environment: TODO diff --git a/src/libc/stdlib/stdlib_p.h b/src/libc/stdlib/stdlib_p.h index ab3caba..1190863 100644 --- a/src/libc/stdlib/stdlib_p.h +++ b/src/libc/stdlib/stdlib_p.h @@ -1,7 +1,7 @@ #ifndef __STDLIB_P_H__ # define __STDLIB_P_H__ -#include +#include #include /* Parse an integer from a string. This is the base function for strtol, diff --git a/src/libc/stdlib/strto_int.c b/src/libc/stdlib/strto_int.c index 4b43124..5c6abd8 100644 --- a/src/libc/stdlib/strto_int.c +++ b/src/libc/stdlib/strto_int.c @@ -2,6 +2,7 @@ #include #include #include +#include /* Parse an integer from a string. Base function for strtol, strtoul, strtoll, and strtoull. This function: @@ -55,12 +56,6 @@ int strto_int(char const * restrict ptr, char ** restrict endptr, int base, valid = true; /* (x = base*x + v) but with overflow checks */ - - /* - ** TODO FIXME: There is a bug with overflows if the unsigned - ** value cannot be represented but the signed value can (which - ** is the case only for LONG_MIN and LLONG_MIN) - */ if(outl) { if(__builtin_umull_overflow(xl, base, &xl)) errno_value = ERANGE; @@ -77,7 +72,7 @@ int strto_int(char const * restrict ptr, char ** restrict endptr, int base, ptr++; } - /* Handle the sign */ + /* Handle sign and range */ if(negative) { /* Only -0 can be represented as unsigned */ if(outl && use_unsigned && xl != 0) @@ -85,9 +80,21 @@ int strto_int(char const * restrict ptr, char ** restrict endptr, int base, if(outll && use_unsigned && xll != 0) errno_value = ERANGE; + /* Handle signed range. Don't use -[L]LONG_MIN, it overflows */ + if(outl && !use_unsigned && xl > LONG_MAX * 1ul + 1) + errno_value = ERANGE; + if(outll && !use_unsigned && xll > LLONG_MAX * 1ull + 1) + errno_value = ERANGE; + xl = -xl; xll = -xll; } + else { + if(outl && !use_unsigned && xl > LONG_MAX * 1ul) + errno_value = ERANGE; + if(outll && !use_unsigned && xll > LLONG_MAX * 1ull) + errno_value = ERANGE; + } if(outl) *outl = xl; if(outll) *outll = xll; diff --git a/src/libc/stdlib/strtol.c b/src/libc/stdlib/strtol.c new file mode 100644 index 0000000..d0cb402 --- /dev/null +++ b/src/libc/stdlib/strtol.c @@ -0,0 +1,10 @@ +#include "stdlib_p.h" +#include + +long int strtol(char const * restrict ptr, char ** restrict endptr, int base) +{ + long n = 0; + int err = strto_int(ptr, endptr, base, &n, NULL, false); + if(err != 0) errno = err; + return n; +} diff --git a/src/libc/stdlib/strtoll.c b/src/libc/stdlib/strtoll.c new file mode 100644 index 0000000..5fc2d5c --- /dev/null +++ b/src/libc/stdlib/strtoll.c @@ -0,0 +1,11 @@ +#include "stdlib_p.h" +#include + +long long int strtoll(char const * restrict ptr, char ** restrict endptr, + int base) +{ + long long n = 0; + int err = strto_int(ptr, endptr, base, NULL, &n, false); + if(err != 0) errno = err; + return n; +} diff --git a/src/libc/stdlib/strtoul.c b/src/libc/stdlib/strtoul.c new file mode 100644 index 0000000..75c5ef0 --- /dev/null +++ b/src/libc/stdlib/strtoul.c @@ -0,0 +1,11 @@ +#include "stdlib_p.h" +#include + +unsigned long int strtoul(char const * restrict ptr, char ** restrict endptr, + int base) +{ + unsigned long n = 0; + int err = strto_int(ptr, endptr, base, (long *)&n, NULL, true); + if(err != 0) errno = err; + return n; +}