From ce4d6717f85eeddd0b374d2829f46548972c5d4e Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Mon, 19 Dec 2022 13:35:45 -0800 Subject: [PATCH] [libc][NFC] make atoi undefined cases match std The standard describes atoi as: "equivalent to atoi: (int)strtol(nptr, (char **)NULL, 10)" Previously, our behavior was slightly different on numbers larger than INT_MAX, but this patch changes it to just do the cast instead. Both of these are valid since the standard says "If the value of the result cannot be represented, the behavior is undefined." But matching existing behavior makes differential fuzzing easier. Reviewed By: sivachandra Differential Revision: https://reviews.llvm.org/D140350 --- libc/src/stdlib/atoi.cpp | 6 ++++-- libc/test/src/stdlib/AtoiTest.h | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/libc/src/stdlib/atoi.cpp b/libc/src/stdlib/atoi.cpp index f61e365..d9bc866 100644 --- a/libc/src/stdlib/atoi.cpp +++ b/libc/src/stdlib/atoi.cpp @@ -13,11 +13,13 @@ namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, atoi, (const char *str)) { - auto result = internal::strtointeger(str, 10); + // This is done because the standard specifies that atoi is identical to + // (int)(strtol). + auto result = internal::strtointeger(str, 10); if (result.has_error()) errno = result.error; - return result; + return static_cast(result); } } // namespace __llvm_libc diff --git a/libc/test/src/stdlib/AtoiTest.h b/libc/test/src/stdlib/AtoiTest.h index 4013800..db48945 100644 --- a/libc/test/src/stdlib/AtoiTest.h +++ b/libc/test/src/stdlib/AtoiTest.h @@ -6,10 +6,13 @@ // //===----------------------------------------------------------------------===// +#include "src/__support/CPP/type_traits.h" #include "utils/UnitTest/Test.h" #include +using __llvm_libc::cpp::is_same_v; + template struct AtoTest : public __llvm_libc::testing::Test { using FunctionT = ReturnT (*)(const char *); @@ -54,6 +57,26 @@ template struct AtoTest : public __llvm_libc::testing::Test { const char *smallest_long_long = "-9223372036854775808"; ASSERT_EQ(func(smallest_long_long), static_cast(LLONG_MIN)); } + + // If this is atoi and the size of int is less than the size of long, then + // we parse as long and cast to int to match existing behavior. This only + // matters for cases where the result would be outside of the int range, and + // those cases are undefined, so we can choose whatever output value we + // want. In this case we have chosen to cast since that matches existing + // implementations and makes differential fuzzing easier, but no user should + // rely on this behavior. + if constexpr (is_same_v && sizeof(ReturnT) < sizeof(long)) { + + static_assert(sizeof(int) == 4); + + const char *bigger_than_biggest_int = "2147483649"; + ASSERT_EQ(func(bigger_than_biggest_int), + static_cast(2147483649)); + + const char *smaller_than_smallest_int = "-2147483649"; + ASSERT_EQ(func(smaller_than_smallest_int), + static_cast(-2147483649)); + } } void nonBaseTenWholeNumbers(FunctionT func) { -- 2.7.4