diff --git a/compiler-rt/lib/asan/asan_interceptors.cc b/compiler-rt/lib/asan/asan_interceptors.cc index 17c58d67ffc5..465e787c90df 100644 --- a/compiler-rt/lib/asan/asan_interceptors.cc +++ b/compiler-rt/lib/asan/asan_interceptors.cc @@ -24,6 +24,14 @@ #include +// Use macro to describe if specific function should be +// intercepted on a given platform. +#if !defined(_WIN32) +# define ASAN_INTERCEPT_STRTOLL 1 +#else +# define ASAN_INTERCEPT_STRTOLL 0 +#endif // ASAN_WINDOWS + // Use extern declarations of intercepted functions on Mac and Windows // to avoid including system headers. #if defined(__APPLE__) || (defined(_WIN32) && !defined(_DLL)) @@ -68,6 +76,11 @@ size_t strlen(const char *s); size_t strnlen(const char *s, size_t maxlen); # endif +// stdlib.h +# if ASAN_INTERCEPT_STRTOLL +long long strtoll(const char *nptr, char **endptr, int base); // NOLINT +# endif + // Windows threads. # if defined(_WIN32) __declspec(dllimport) @@ -635,6 +648,42 @@ INTERCEPTOR(size_t, strnlen, const char *s, size_t maxlen) { } #endif +# if ASAN_INTERCEPT_STRTOLL +// Returns pointer to first character of "nptr" after skipping +// leading blanks and optional +/- sign. +static char *SkipBlanksAndSign(const char *nptr) { + while (IsSpace(*nptr)) nptr++; + if (*nptr == '+' || *nptr == '-') nptr++; + return (char*)nptr; +} + +INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT + char **endptr, int base) { + ENSURE_ASAN_INITED(); + if (!FLAG_replace_str) { + return REAL(strtoll)(nptr, endptr, base); + } + char *real_endptr; + long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT + if (endptr != NULL) { + *endptr = real_endptr; + } + // If base has unsupported value, strtoll can exit with EINVAL + // without reading any characters. So do additional checks only + // if base is valid. + if (base == 0 || (2 <= base && base <= 36)) { + if (real_endptr == nptr) { + // No digits were found, find out the last symbol read by strtoll + // on our own. + real_endptr = SkipBlanksAndSign(nptr); + } + CHECK(real_endptr >= nptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + } + return result; +} +#endif // ASAN_INTERCEPT_STRTOLL + #if defined(_WIN32) INTERCEPTOR_WINAPI(DWORD, CreateThread, void* security, size_t stack_size, @@ -694,6 +743,10 @@ void InitializeAsanInterceptors() { CHECK(INTERCEPT_FUNCTION(strnlen)); #endif +#if ASAN_INTERCEPT_STRTOLL + CHECK(INTERCEPT_FUNCTION(strtoll)); +#endif + // Intecept signal- and jump-related functions. CHECK(INTERCEPT_FUNCTION(longjmp)); #if !defined(ANDROID) && !defined(_WIN32) diff --git a/compiler-rt/lib/asan/tests/asan_test.cc b/compiler-rt/lib/asan/tests/asan_test.cc index 5094ee49d606..a446db000a24 100644 --- a/compiler-rt/lib/asan/tests/asan_test.cc +++ b/compiler-rt/lib/asan/tests/asan_test.cc @@ -1349,6 +1349,46 @@ TEST(AddressSanitizer, StrArgsOverlapTest) { free(str); } +TEST(AddressSanitizer, StrtollOOBTest) { + char *array = MallocAndMemsetString(3); + char *endptr = NULL; + array[0] = '1'; + array[1] = '2'; + array[2] = '3'; + // Invalid pointer to the string. + EXPECT_DEATH(strtoll(array + 3, NULL, 0), RightOOBErrorMessage(0)); + EXPECT_DEATH(strtoll(array - 1, NULL, 0), LeftOOBErrorMessage(1)); + // Buffer overflow if there is no terminating null (depends on base). + Ident(strtoll(array, &endptr, 3)); + EXPECT_EQ(array + 2, endptr); + EXPECT_DEATH(strtoll(array, NULL, 0), RightOOBErrorMessage(0)); + array[2] = 'z'; + Ident(strtoll(array, &endptr, 35)); + EXPECT_EQ(array + 2, endptr); + EXPECT_DEATH(strtoll(array, NULL, 36), RightOOBErrorMessage(0)); + // Add terminating zero to get rid of overflow. + array[2] = '\0'; + Ident(strtoll(array, NULL, 36)); + // Don't check for overflow if base is invalid. + Ident(strtoll(array - 1, NULL, -1)); + Ident(strtoll(array + 3, NULL, 1)); + // Sometimes we need to detect overflow if no digits are found. + array[0] = array[1] = array[2] = ' '; + EXPECT_DEATH(strtoll(array, NULL, 0), RightOOBErrorMessage(0)); + array[2] = '+'; + EXPECT_DEATH(strtoll(array, NULL, 0), RightOOBErrorMessage(0)); + array[2] = '-'; + EXPECT_DEATH(strtoll(array, NULL, 0), RightOOBErrorMessage(0)); + array[1] = '+'; + Ident(strtoll(array, NULL, 0)); + array[1] = array[2] = 'z'; + Ident(strtoll(array, &endptr, 0)); + EXPECT_EQ(array, endptr); + Ident(strtoll(array + 2, NULL, 0)); + EXPECT_EQ(array, endptr); + delete array; +} + // At the moment we instrument memcpy/memove/memset calls at compile time so we // can't handle OOB error if these functions are called by pointer, see disabled // MemIntrinsicCallByPointerTest below