From b7ed12ebd38bf3c70b51d5b40ef71d0bbb1860e2 Mon Sep 17 00:00:00 2001 From: Juha Niskanen Date: Thu, 10 Nov 2016 06:09:57 -0600 Subject: [PATCH] Patch brings strtol() and related functions more conformant with POSIX. Corner cases like strtol(-2147483648, NULL, 10) now pass clang -fsanitize=integer without warnings. --- libc/stdlib/lib_strtol.c | 27 ++++++++++++++++++++++++--- libc/stdlib/lib_strtoll.c | 29 +++++++++++++++++++++++++---- libc/stdlib/lib_strtoul.c | 7 ++++--- libc/stdlib/lib_strtoull.c | 7 ++++--- 4 files changed, 57 insertions(+), 13 deletions(-) diff --git a/libc/stdlib/lib_strtol.c b/libc/stdlib/lib_strtol.c index 8c7f639901..2c6c6a24de 100644 --- a/libc/stdlib/lib_strtol.c +++ b/libc/stdlib/lib_strtol.c @@ -41,6 +41,7 @@ #include #include +#include #include "libc.h" @@ -60,7 +61,13 @@ * nptr to a long integer value according to the given base, which must be * between 2 and 36 inclusive, or be the special value 0. * - * Warning: does not check for integer overflow! + * Returns: + * - The converted value, if the base and number are valid + * - 0 if an error occurs, and set errno to: + * * EINVAL if base < 2 or base > 36 + * - LONG_MIN or LONG_MAX, of correct sign, if an overflow occurs, + * and set errno to: + * * ERANGE if the number cannot be represented using long * ****************************************************************************/ @@ -91,11 +98,25 @@ long strtol(FAR const char *nptr, FAR char **endptr, int base) accum = strtoul(nptr, endptr, base); - /* Correct the sign of the result */ + /* Correct the sign of the result and check for overflow */ if (negate) { - return -(long)accum; + const unsigned long limit = ((unsigned long)-(LONG_MIN + 1)) + 1; + + if (accum > limit) + { + set_errno(ERANGE); + return LONG_MIN; + } + + return (accum == limit) ? LONG_MIN : -(long)accum; + } + + if (accum > LONG_MAX) + { + set_errno(ERANGE); + return LONG_MAX; } } diff --git a/libc/stdlib/lib_strtoll.c b/libc/stdlib/lib_strtoll.c index bc8cc2eb6f..a5f8b2cdc3 100644 --- a/libc/stdlib/lib_strtoll.c +++ b/libc/stdlib/lib_strtoll.c @@ -41,6 +41,7 @@ #include #include +#include #include "libc.h" @@ -58,11 +59,17 @@ * Name: strtoll * * Description: - * The strtol() function converts the initial part of the string in + * The strtoll() function converts the initial part of the string in * nptr to a long long integer value according to the given base, which * must be between 2 and 36 inclusive, or be the special value 0. * - * Warning: does not check for integer overflow! + * Returns: + * - The converted value, if the base and number are valid + * - 0 if an error occurs, and set errno to: + * * EINVAL if base < 2 or base > 36 + * - LLONG_MIN or LLONG_MAX, of correct sign, if an overflow occurs, + * and set errno to: + * * ERANGE if the number cannot be represented using long long * ****************************************************************************/ @@ -93,11 +100,25 @@ long long strtoll(FAR const char *nptr, FAR char **endptr, int base) accum = strtoull(nptr, endptr, base); - /* Correct the sign of the result */ + /* Correct the sign of the result and check for overflow */ if (negate) { - return -(long long)accum; + const unsigned long long limit = ((unsigned long long)-(LLONG_MIN + 1)) + 1; + + if (accum > limit) + { + set_errno(ERANGE); + return LLONG_MIN; + } + + return (accum == limit) ? LLONG_MIN : -(long long)accum; + } + + if (accum > LLONG_MAX) + { + set_errno(ERANGE); + return LLONG_MAX; } } diff --git a/libc/stdlib/lib_strtoul.c b/libc/stdlib/lib_strtoul.c index 30ffbd31c3..1dec49e146 100644 --- a/libc/stdlib/lib_strtoul.c +++ b/libc/stdlib/lib_strtoul.c @@ -52,14 +52,15 @@ * Name: strtoul * * Description: - * The strtol() function converts the initial part of the string in + * The strtoul() function converts the initial part of the string in * nptr to a long unsigned integer value according to the given base, which * must be between 2 and 36 inclusive, or be the special value 0. * * Returns: * - The converted value, if the base and number are valid - * - 0 if an error occurs, and seterrno to: + * - 0 if an error occurs, and set errno to: * * EINVAL if base < 2 or base > 36 + * - ULONG_MAX if an overflow occurs, and set errno to: * * ERANGE if the number cannot be represented using unsigned long * ****************************************************************************/ @@ -99,7 +100,7 @@ unsigned long strtoul(FAR const char *nptr, FAR char **endptr, int base) if (accum < prev) { set_errno(ERANGE); - accum = 0; + accum = ULONG_MAX; break; } } diff --git a/libc/stdlib/lib_strtoull.c b/libc/stdlib/lib_strtoull.c index fab588891b..fe13b63249 100644 --- a/libc/stdlib/lib_strtoull.c +++ b/libc/stdlib/lib_strtoull.c @@ -55,14 +55,15 @@ * Name: strtoull * * Description: - * The strtol() function converts the initial part of the string in + * The strtoull() function converts the initial part of the string in * nptr to a long unsigned integer value according to the given base, which * must be between 2 and 36 inclusive, or be the special value 0. * * Returns: * - The converted value, if the base and number are valid - * - 0 if an error occurs, and seterrno to: + * - 0 if an error occurs, and set errno to: * * EINVAL if base < 2 or base > 36 + * - ULLONG_MAX if an overflow occurs, and set errno to: * * ERANGE if the number cannot be represented using unsigned long long * ****************************************************************************/ @@ -102,7 +103,7 @@ unsigned long long strtoull(FAR const char *nptr, FAR char **endptr, int base) if (accum < prev) { set_errno(ERANGE); - accum = 0; + accum = ULLONG_MAX; break; } }