diff options
-rw-r--r-- | compiler-rt/lib/asan/asan_interceptors.cc | 52 | ||||
-rw-r--r-- | compiler-rt/lib/asan/tests/asan_test.cc | 47 |
2 files changed, 69 insertions, 30 deletions
diff --git a/compiler-rt/lib/asan/asan_interceptors.cc b/compiler-rt/lib/asan/asan_interceptors.cc index f10ae981a33..8228121374a 100644 --- a/compiler-rt/lib/asan/asan_interceptors.cc +++ b/compiler-rt/lib/asan/asan_interceptors.cc @@ -89,6 +89,7 @@ size_t strnlen(const char *s, size_t maxlen); # endif // stdlib.h +long strtol(const char *nptr, char **endptr, int base); // NOLINT # if ASAN_INTERCEPT_STRTOLL long long strtoll(const char *nptr, char **endptr, int base); // NOLINT # endif @@ -659,15 +660,24 @@ INTERCEPTOR(size_t, strnlen, const char *s, size_t maxlen) { } #endif // ASAN_INTERCEPT_STRNLEN -# 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; +static inline bool IsValidStrtolBase(int base) { + return (base == 0) || (2 <= base && base <= 36); +} + +static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { + CHECK(endptr != NULL); + if (nptr == *endptr) { + // No digits were found at strtol call, we need to find out the last + // symbol accessed by strtoll on our own. + // We get this symbol by skipping leading blanks and optional +/- sign. + while (IsSpace(*nptr)) nptr++; + if (*nptr == '+' || *nptr == '-') nptr++; + *endptr = (char*)nptr; + } + CHECK(*endptr >= nptr); } +# if ASAN_INTERCEPT_STRTOLL INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT char **endptr, int base) { ENSURE_ASAN_INITED(); @@ -682,19 +692,32 @@ INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT // 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); + if (IsValidStrtolBase(base)) { + FixRealStrtolEndptr(nptr, &real_endptr); ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); } return result; } #endif // ASAN_INTERCEPT_STRTOLL +INTERCEPTOR(long, strtol, const char *nptr, // NOLINT + char **endptr, int base) { + ENSURE_ASAN_INITED(); + if (!FLAG_replace_str) { + return REAL(strtol)(nptr, endptr, base); + } + char *real_endptr; + long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT + if (endptr != NULL) { + *endptr = real_endptr; + } + if (IsValidStrtolBase(base)) { + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + } + return result; +} + #if defined(_WIN32) INTERCEPTOR_WINAPI(DWORD, CreateThread, void* security, size_t stack_size, @@ -754,6 +777,7 @@ void InitializeAsanInterceptors() { CHECK(INTERCEPT_FUNCTION(strnlen)); #endif + CHECK(INTERCEPT_FUNCTION(strtol)); #if ASAN_INTERCEPT_STRTOLL CHECK(INTERCEPT_FUNCTION(strtoll)); #endif diff --git a/compiler-rt/lib/asan/tests/asan_test.cc b/compiler-rt/lib/asan/tests/asan_test.cc index a446db000a2..b611d52c744 100644 --- a/compiler-rt/lib/asan/tests/asan_test.cc +++ b/compiler-rt/lib/asan/tests/asan_test.cc @@ -1349,46 +1349,61 @@ TEST(AddressSanitizer, StrArgsOverlapTest) { free(str); } -TEST(AddressSanitizer, StrtollOOBTest) { +void CallStrtol(const char *nptr, char **endptr, int base) { + Ident(strtol(nptr, endptr, base)); +} +void CallStrtoll(const char *nptr, char **endptr, int base) { + Ident(strtoll(nptr, endptr, base)); +} +typedef void(*PointerToCallStrtol)(const char*, char**, int); + +void RunStrtolOOBTest(PointerToCallStrtol Strtol) { 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)); + EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBErrorMessage(0)); + EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBErrorMessage(1)); // Buffer overflow if there is no terminating null (depends on base). - Ident(strtoll(array, &endptr, 3)); + Strtol(array, &endptr, 3); EXPECT_EQ(array + 2, endptr); - EXPECT_DEATH(strtoll(array, NULL, 0), RightOOBErrorMessage(0)); + EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0)); array[2] = 'z'; - Ident(strtoll(array, &endptr, 35)); + Strtol(array, &endptr, 35); EXPECT_EQ(array + 2, endptr); - EXPECT_DEATH(strtoll(array, NULL, 36), RightOOBErrorMessage(0)); + EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBErrorMessage(0)); // Add terminating zero to get rid of overflow. array[2] = '\0'; - Ident(strtoll(array, NULL, 36)); + Strtol(array, NULL, 36); // Don't check for overflow if base is invalid. - Ident(strtoll(array - 1, NULL, -1)); - Ident(strtoll(array + 3, NULL, 1)); + Strtol(array - 1, NULL, -1); + Strtol(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)); + EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0)); array[2] = '+'; - EXPECT_DEATH(strtoll(array, NULL, 0), RightOOBErrorMessage(0)); + EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0)); array[2] = '-'; - EXPECT_DEATH(strtoll(array, NULL, 0), RightOOBErrorMessage(0)); + EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0)); array[1] = '+'; - Ident(strtoll(array, NULL, 0)); + Strtol(array, NULL, 0); array[1] = array[2] = 'z'; - Ident(strtoll(array, &endptr, 0)); + Strtol(array, &endptr, 0); EXPECT_EQ(array, endptr); - Ident(strtoll(array + 2, NULL, 0)); + Strtol(array + 2, NULL, 0); EXPECT_EQ(array, endptr); delete array; } +TEST(AddressSanitizer, StrtollOOBTest) { + RunStrtolOOBTest(&CallStrtoll); +} +TEST(AddressSanitizer, StrtolOOBTest) { + RunStrtolOOBTest(&CallStrtol); +} + // 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 |