[ASan] add interceptor for strtol

llvm-svn: 153444
This commit is contained in:
Alexey Samsonov 2012-03-26 16:42:22 +00:00
parent c29ebc36b1
commit 0a4f8dc0cb
2 changed files with 69 additions and 30 deletions

View File

@ -89,6 +89,7 @@ size_t strnlen(const char *s, size_t maxlen);
# endif # endif
// stdlib.h // stdlib.h
long strtol(const char *nptr, char **endptr, int base); // NOLINT
# if ASAN_INTERCEPT_STRTOLL # if ASAN_INTERCEPT_STRTOLL
long long strtoll(const char *nptr, char **endptr, int base); // NOLINT long long strtoll(const char *nptr, char **endptr, int base); // NOLINT
# endif # endif
@ -659,15 +660,24 @@ INTERCEPTOR(size_t, strnlen, const char *s, size_t maxlen) {
} }
#endif // ASAN_INTERCEPT_STRNLEN #endif // ASAN_INTERCEPT_STRNLEN
# if ASAN_INTERCEPT_STRTOLL static inline bool IsValidStrtolBase(int base) {
// Returns pointer to first character of "nptr" after skipping return (base == 0) || (2 <= base && base <= 36);
// leading blanks and optional +/- sign.
static char *SkipBlanksAndSign(const char *nptr) {
while (IsSpace(*nptr)) nptr++;
if (*nptr == '+' || *nptr == '-') nptr++;
return (char*)nptr;
} }
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 INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT
char **endptr, int base) { char **endptr, int base) {
ENSURE_ASAN_INITED(); 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 // If base has unsupported value, strtoll can exit with EINVAL
// without reading any characters. So do additional checks only // without reading any characters. So do additional checks only
// if base is valid. // if base is valid.
if (base == 0 || (2 <= base && base <= 36)) { if (IsValidStrtolBase(base)) {
if (real_endptr == nptr) { FixRealStrtolEndptr(nptr, &real_endptr);
// 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); ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1);
} }
return result; return result;
} }
#endif // ASAN_INTERCEPT_STRTOLL #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) #if defined(_WIN32)
INTERCEPTOR_WINAPI(DWORD, CreateThread, INTERCEPTOR_WINAPI(DWORD, CreateThread,
void* security, size_t stack_size, void* security, size_t stack_size,
@ -754,6 +777,7 @@ void InitializeAsanInterceptors() {
CHECK(INTERCEPT_FUNCTION(strnlen)); CHECK(INTERCEPT_FUNCTION(strnlen));
#endif #endif
CHECK(INTERCEPT_FUNCTION(strtol));
#if ASAN_INTERCEPT_STRTOLL #if ASAN_INTERCEPT_STRTOLL
CHECK(INTERCEPT_FUNCTION(strtoll)); CHECK(INTERCEPT_FUNCTION(strtoll));
#endif #endif

View File

@ -1349,46 +1349,61 @@ TEST(AddressSanitizer, StrArgsOverlapTest) {
free(str); 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 *array = MallocAndMemsetString(3);
char *endptr = NULL; char *endptr = NULL;
array[0] = '1'; array[0] = '1';
array[1] = '2'; array[1] = '2';
array[2] = '3'; array[2] = '3';
// Invalid pointer to the string. // Invalid pointer to the string.
EXPECT_DEATH(strtoll(array + 3, NULL, 0), RightOOBErrorMessage(0)); EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBErrorMessage(0));
EXPECT_DEATH(strtoll(array - 1, NULL, 0), LeftOOBErrorMessage(1)); EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBErrorMessage(1));
// Buffer overflow if there is no terminating null (depends on base). // 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_EQ(array + 2, endptr);
EXPECT_DEATH(strtoll(array, NULL, 0), RightOOBErrorMessage(0)); EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
array[2] = 'z'; array[2] = 'z';
Ident(strtoll(array, &endptr, 35)); Strtol(array, &endptr, 35);
EXPECT_EQ(array + 2, endptr); 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. // Add terminating zero to get rid of overflow.
array[2] = '\0'; array[2] = '\0';
Ident(strtoll(array, NULL, 36)); Strtol(array, NULL, 36);
// Don't check for overflow if base is invalid. // Don't check for overflow if base is invalid.
Ident(strtoll(array - 1, NULL, -1)); Strtol(array - 1, NULL, -1);
Ident(strtoll(array + 3, NULL, 1)); Strtol(array + 3, NULL, 1);
// Sometimes we need to detect overflow if no digits are found. // Sometimes we need to detect overflow if no digits are found.
array[0] = array[1] = array[2] = ' '; array[0] = array[1] = array[2] = ' ';
EXPECT_DEATH(strtoll(array, NULL, 0), RightOOBErrorMessage(0)); EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
array[2] = '+'; array[2] = '+';
EXPECT_DEATH(strtoll(array, NULL, 0), RightOOBErrorMessage(0)); EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
array[2] = '-'; array[2] = '-';
EXPECT_DEATH(strtoll(array, NULL, 0), RightOOBErrorMessage(0)); EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
array[1] = '+'; array[1] = '+';
Ident(strtoll(array, NULL, 0)); Strtol(array, NULL, 0);
array[1] = array[2] = 'z'; array[1] = array[2] = 'z';
Ident(strtoll(array, &endptr, 0)); Strtol(array, &endptr, 0);
EXPECT_EQ(array, endptr); EXPECT_EQ(array, endptr);
Ident(strtoll(array + 2, NULL, 0)); Strtol(array + 2, NULL, 0);
EXPECT_EQ(array, endptr); EXPECT_EQ(array, endptr);
delete array; 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 // 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 // can't handle OOB error if these functions are called by pointer, see disabled
// MemIntrinsicCallByPointerTest below // MemIntrinsicCallByPointerTest below