[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
// 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

View File

@ -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