sanitizer: new "strict_string_checks" run-time flag

This patch is related to Issue 346: moar string interceptors: strstr, strcasestr, strcspn, strpbrk
As was suggested in original review http://reviews.llvm.org/D6056 a new "strict_string_checks" run-time flag introduced.
The flag support applied for existing common, asan, msan and tsan interceptors. New asan tests added.

Change by Maria Guseva reviewed in http://reviews.llvm.org/D7123

llvm-svn: 234187
This commit is contained in:
Dmitry Vyukov 2015-04-06 18:00:26 +00:00
parent 9d3949cca3
commit 1e5b9f4131
15 changed files with 643 additions and 90 deletions

View File

@ -75,6 +75,13 @@ struct AsanInterceptorContext {
#define ASAN_WRITE_RANGE(ctx, offset, size) \
ACCESS_MEMORY_RANGE(ctx, offset, size, true)
#define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \
ASAN_READ_RANGE((ctx), (s), \
common_flags()->strict_string_checks ? (len) + 1 : (n))
#define ASAN_READ_STRING(ctx, s, n) \
ASAN_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
// Behavior of functions like "memcpy" or "strcpy" is undefined
// if memory intervals overlap. We report error in this case.
// Macro is used to avoid creation of new frames.
@ -475,8 +482,9 @@ INTERCEPTOR(char*, strchr, const char *str, int c) {
ENSURE_ASAN_INITED();
char *result = REAL(strchr)(str, c);
if (flags()->replace_str) {
uptr bytes_read = (result ? result - str : REAL(strlen)(str)) + 1;
ASAN_READ_RANGE(ctx, str, bytes_read);
uptr len = REAL(strlen)(str);
uptr bytes_read = (result ? result - str : len) + 1;
ASAN_READ_STRING_OF_LEN(ctx, str, len, bytes_read);
}
return result;
}
@ -505,7 +513,7 @@ INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT
uptr from_length = REAL(strlen)(from);
ASAN_READ_RANGE(ctx, from, from_length + 1);
uptr to_length = REAL(strlen)(to);
ASAN_READ_RANGE(ctx, to, to_length);
ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length);
ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1);
// If the copying actually happens, the |from| string should not overlap
// with the resulting string starting at |to|, which has a length of
@ -527,7 +535,7 @@ INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
uptr copy_length = Min(size, from_length + 1);
ASAN_READ_RANGE(ctx, from, copy_length);
uptr to_length = REAL(strlen)(to);
ASAN_READ_RANGE(ctx, to, to_length);
ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length);
ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1);
if (from_length > 0) {
CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1,
@ -629,23 +637,6 @@ INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) {
}
#endif // ASAN_INTERCEPT_STRNLEN
static inline bool IsValidStrtolBase(int base) {
return (base == 0) || (2 <= base && base <= 36);
}
static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) {
CHECK(endptr);
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 = const_cast<char *>(nptr);
}
CHECK(*endptr >= nptr);
}
INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
char **endptr, int base) {
void *ctx;
@ -656,13 +647,7 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
}
char *real_endptr;
long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT
if (endptr != 0) {
*endptr = real_endptr;
}
if (IsValidStrtolBase(base)) {
FixRealStrtolEndptr(nptr, &real_endptr);
ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
}
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return result;
}
@ -683,7 +668,7 @@ INTERCEPTOR(int, atoi, const char *nptr) {
// different from int). So, we just imitate this behavior.
int result = REAL(strtol)(nptr, &real_endptr, 10);
FixRealStrtolEndptr(nptr, &real_endptr);
ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
return result;
}
@ -700,7 +685,7 @@ INTERCEPTOR(long, atol, const char *nptr) { // NOLINT
char *real_endptr;
long result = REAL(strtol)(nptr, &real_endptr, 10); // NOLINT
FixRealStrtolEndptr(nptr, &real_endptr);
ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
return result;
}
@ -715,16 +700,7 @@ INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT
}
char *real_endptr;
long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT
if (endptr != 0) {
*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 (IsValidStrtolBase(base)) {
FixRealStrtolEndptr(nptr, &real_endptr);
ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
}
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return result;
}
@ -738,7 +714,7 @@ INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT
char *real_endptr;
long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT
FixRealStrtolEndptr(nptr, &real_endptr);
ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
return result;
}
#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL

View File

@ -290,9 +290,6 @@ void RunStrCmpTest(PointerToStrCmp StrCmp) {
Ident(StrCmp(s1, s2));
Ident(StrCmp(s1, s2 + size - 1));
Ident(StrCmp(s1 + size - 1, s2 + size - 1));
s1[size - 1] = 'z';
s2[size - 1] = 'x';
Ident(StrCmp(s1, s2));
// One of arguments points to not allocated memory.
EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBReadMessage(1));
EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBReadMessage(1));
@ -371,17 +368,14 @@ TEST(AddressSanitizer, StrCatOOBTest) {
// One of arguments points to not allocated memory.
EXPECT_DEATH(strcat(to - 1, from), LeftOOBAccessMessage(1));
EXPECT_DEATH(strcat(to, from - 1), LeftOOBReadMessage(1));
EXPECT_DEATH(strcat(to + to_size, from), RightOOBWriteMessage(0));
EXPECT_DEATH(strcat(to, from + from_size), RightOOBReadMessage(0));
// "from" is not zero-terminated.
from[from_size - 1] = 'z';
EXPECT_DEATH(strcat(to, from), RightOOBReadMessage(0));
from[from_size - 1] = '\0';
// "to" is not zero-terminated.
memset(to, 'z', to_size);
EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
// "to" is too short to fit "from".
memset(to, 'z', to_size);
to[to_size - from_size + 1] = '\0';
EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
// length of "to" is just enough.
@ -409,7 +403,6 @@ TEST(AddressSanitizer, StrNCatOOBTest) {
// One of arguments points to not allocated memory.
EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1));
EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBReadMessage(1));
EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBWriteMessage(0));
EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0));
memset(from, 'z', from_size);
@ -417,8 +410,6 @@ TEST(AddressSanitizer, StrNCatOOBTest) {
to[0] = '\0';
// "from" is too short.
EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0));
// "to" is not zero-terminated.
EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBWriteMessage(0));
// "to" is too short to fit "from".
to[0] = 'z';
to[to_size - from_size + 1] = '\0';
@ -508,20 +499,15 @@ void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
EXPECT_DEATH(Atoi(array - 1), LeftOOBReadMessage(1));
// Die if a buffer doesn't have terminating NULL.
EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
// Make last symbol a terminating NULL or other non-digit.
// Make last symbol a terminating NULL
array[9] = '\0';
Atoi(array);
array[9] = 'a';
Atoi(array);
Atoi(array + 9);
// Sometimes we need to detect overflow if no digits are found.
memset(array, ' ', 10);
EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
array[9] = '-';
EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0));
array[8] = '-';
Atoi(array);
free(array);
}
@ -546,7 +532,6 @@ 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';
@ -554,19 +539,12 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBReadMessage(0));
EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBReadMessage(1));
// Buffer overflow if there is no terminating null (depends on base).
Strtol(array, &endptr, 3);
EXPECT_EQ(array + 2, endptr);
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
array[2] = 'z';
Strtol(array, &endptr, 35);
EXPECT_EQ(array + 2, endptr);
EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0));
// Add terminating zero to get rid of overflow.
array[2] = '\0';
Strtol(array, NULL, 36);
// Don't check for overflow if base is invalid.
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(Strtol(array, NULL, 0), RightOOBReadMessage(0));
@ -574,13 +552,6 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
array[2] = '-';
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
array[1] = '+';
Strtol(array, NULL, 0);
array[1] = array[2] = 'z';
Strtol(array, &endptr, 0);
EXPECT_EQ(array, endptr);
Strtol(array + 2, NULL, 0);
EXPECT_EQ(array, endptr);
free(array);
}

View File

@ -94,6 +94,13 @@ bool IsInInterceptorScope() {
if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \
} while (0);
#define CHECK_UNPOISONED_STRING_OF_LEN(x, len, n) \
CHECK_UNPOISONED((x), \
common_flags()->strict_string_checks ? (len) + 1 : (n) )
#define CHECK_UNPOISONED_STRING(x, n) \
CHECK_UNPOISONED_STRING_OF_LEN((x), internal_strlen(x), (n))
INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) {
ENSURE_MSAN_INITED();
SIZE_T res = REAL(fread)(ptr, size, nmemb, file);
@ -118,6 +125,7 @@ INTERCEPTOR(SIZE_T, fread_unlocked, void *ptr, SIZE_T size, SIZE_T nmemb,
INTERCEPTOR(SSIZE_T, readlink, const char *path, char *buf, SIZE_T bufsiz) {
ENSURE_MSAN_INITED();
CHECK_UNPOISONED_STRING(path, 0)
SSIZE_T res = REAL(readlink)(path, buf, bufsiz);
if (res > 0)
__msan_unpoison(buf, res);
@ -283,13 +291,11 @@ INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T n) {
return res;
}
// FIXME: Add stricter shadow checks in str* interceptors (ex.: strcpy should
// check the shadow of the terminating \0 byte).
INTERCEPTOR(char *, strcpy, char *dest, const char *src) { // NOLINT
ENSURE_MSAN_INITED();
GET_STORE_STACK_TRACE;
SIZE_T n = REAL(strlen)(src);
CHECK_UNPOISONED_STRING(src + n, 0);
char *res = REAL(strcpy)(dest, src); // NOLINT
CopyShadowAndOrigin(dest, src, n + 1, &stack);
return res;
@ -311,6 +317,7 @@ INTERCEPTOR(char *, stpcpy, char *dest, const char *src) { // NOLINT
ENSURE_MSAN_INITED();
GET_STORE_STACK_TRACE;
SIZE_T n = REAL(strlen)(src);
CHECK_UNPOISONED_STRING(src + n, 0);
char *res = REAL(stpcpy)(dest, src); // NOLINT
CopyShadowAndOrigin(dest, src, n + 1, &stack);
return res;
@ -322,6 +329,7 @@ INTERCEPTOR(char *, strdup, char *src) {
// On FreeBSD strdup() leverages strlen().
InterceptorScope interceptor_scope;
SIZE_T n = REAL(strlen)(src);
CHECK_UNPOISONED_STRING(src + n, 0);
char *res = REAL(strdup)(src);
CopyShadowAndOrigin(res, src, n + 1, &stack);
return res;
@ -332,6 +340,7 @@ INTERCEPTOR(char *, __strdup, char *src) {
ENSURE_MSAN_INITED();
GET_STORE_STACK_TRACE;
SIZE_T n = REAL(strlen)(src);
CHECK_UNPOISONED_STRING(src + n, 0);
char *res = REAL(__strdup)(src);
CopyShadowAndOrigin(res, src, n + 1, &stack);
return res;
@ -381,6 +390,8 @@ INTERCEPTOR(char *, strcat, char *dest, const char *src) { // NOLINT
GET_STORE_STACK_TRACE;
SIZE_T src_size = REAL(strlen)(src);
SIZE_T dest_size = REAL(strlen)(dest);
CHECK_UNPOISONED_STRING(src + src_size, 0);
CHECK_UNPOISONED_STRING(dest + dest_size, 0);
char *res = REAL(strcat)(dest, src); // NOLINT
CopyShadowAndOrigin(dest + dest_size, src, src_size + 1, &stack);
return res;
@ -391,6 +402,7 @@ INTERCEPTOR(char *, strncat, char *dest, const char *src, SIZE_T n) { // NOLINT
GET_STORE_STACK_TRACE;
SIZE_T dest_size = REAL(strlen)(dest);
SIZE_T copy_size = REAL(strnlen)(src, n);
CHECK_UNPOISONED_STRING(dest + dest_size, 0);
char *res = REAL(strncat)(dest, src, n); // NOLINT
CopyShadowAndOrigin(dest + dest_size, src, copy_size, &stack);
__msan_unpoison(dest + dest_size + copy_size, 1); // \0
@ -667,6 +679,7 @@ static void UnpoisonEnviron() {
INTERCEPTOR(int, setenv, const char *name, const char *value, int overwrite) {
ENSURE_MSAN_INITED();
CHECK_UNPOISONED_STRING(name, 0)
int res = REAL(setenv)(name, value, overwrite);
if (!res) UnpoisonEnviron();
return res;

View File

@ -102,6 +102,13 @@
#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0)
#endif
#define COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, n) \
COMMON_INTERCEPTOR_READ_RANGE((ctx), (s), \
common_flags()->strict_string_checks ? (len) + 1 : (n) )
#define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n) \
COMMON_INTERCEPTOR_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
#ifndef COMMON_INTERCEPTOR_ON_DLOPEN
#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) {}
#endif
@ -159,7 +166,8 @@ UNUSED static void DeleteInterceptorMetadata(void *addr) {
INTERCEPTOR(char*, textdomain, const char *domainname) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, textdomain, domainname);
char* domain = REAL(textdomain)(domainname);
COMMON_INTERCEPTOR_READ_STRING(ctx, domainname, 0);
char *domain = REAL(textdomain)(domainname);
if (domain) {
COMMON_INTERCEPTOR_INITIALIZE_RANGE(domain, REAL(strlen)(domain) + 1);
}
@ -185,8 +193,8 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
c2 = (unsigned char)s2[i];
if (c1 != c2 || c1 == '\0') break;
}
COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1);
COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1);
COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
return CharCmpX(c1, c2);
}
@ -231,8 +239,8 @@ INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) {
c2 = (unsigned char)s2[i];
if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
}
COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1);
COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1);
COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
return CharCaseCmp(c1, c2);
}
@ -727,12 +735,12 @@ INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) {
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(strptime)(s, format, tm);
if (res) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, s, res - s);
COMMON_INTERCEPTOR_READ_STRING(ctx, s, res ? res - s : 0);
if (res && tm) {
// Do not call unpoison_tm here, because strptime does not, in fact,
// initialize the entire struct tm. For example, tm_zone pointer is left
// uninitialized.
if (tm) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm));
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm));
}
return res;
}
@ -1513,6 +1521,7 @@ INTERCEPTOR(int, glob, const char *pattern, int flags,
__sanitizer_glob_t *pglob) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob);
COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
__sanitizer_glob_t glob_copy = {
0, 0, 0,
0, wrapped_gl_closedir, wrapped_gl_readdir,
@ -1543,6 +1552,7 @@ INTERCEPTOR(int, glob64, const char *pattern, int flags,
__sanitizer_glob_t *pglob) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob);
COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
__sanitizer_glob_t glob_copy = {
0, 0, 0,
0, wrapped_gl_closedir, wrapped_gl_readdir,
@ -1689,6 +1699,7 @@ INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) {
INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, inet_pton, af, src, dst);
COMMON_INTERCEPTOR_READ_STRING(ctx, src, 0);
// FIXME: figure out read size based on the address family.
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
@ -2359,6 +2370,37 @@ INTERCEPTOR(char *, get_current_dir_name, int fake) {
#define INIT_GET_CURRENT_DIR_NAME
#endif
UNUSED static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) {
CHECK(endptr);
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 = const_cast<char *>(nptr);
}
CHECK(*endptr >= nptr);
}
UNUSED static inline void StrtolFixAndCheck(void *ctx, const char *nptr,
char **endptr, char *real_endptr, int base) {
if (endptr != 0) {
*endptr = real_endptr;
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
}
// If base has unsupported value, strtol can exit with EINVAL
// without reading any characters. So do additional checks only
// if base is valid.
bool is_valid_base = (base == 0) || (2 <= base && base <= 36);
if (is_valid_base) {
FixRealStrtolEndptr(nptr, &real_endptr);
}
COMMON_INTERCEPTOR_READ_STRING(ctx, nptr, is_valid_base ?
(real_endptr - nptr) + 1 : 0);
}
#if SANITIZER_INTERCEPT_STRTOIMAX
INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
void *ctx;
@ -2366,8 +2408,9 @@ INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
INTMAX_T res = REAL(strtoimax)(nptr, endptr, base);
if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
char *real_endptr;
INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base);
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return res;
}
@ -2377,8 +2420,9 @@ INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
INTMAX_T res = REAL(strtoumax)(nptr, endptr, base);
if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
char *real_endptr;
INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base);
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return res;
}
@ -3642,6 +3686,7 @@ INTERCEPTOR(char *, tempnam, char *dir, char *pfx) {
INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name);
COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name);
return REAL(pthread_setname_np)(thread, name);
}
@ -4699,6 +4744,7 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
void *ctx;
COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag);
if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag);
void *res = REAL(dlopen)(filename, flag);
COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res);

View File

@ -151,3 +151,5 @@ COMMON_FLAG(const char *, stack_trace_format, "DEFAULT",
"Use DEFAULT to get default format.")
COMMON_FLAG(bool, no_huge_pages_for_shadow, true,
"If true, the shadow is not allowed to use huge pages. ")
COMMON_FLAG(bool, strict_string_checks, false,
"If set check that string arguments are properly null-terminated")

View File

@ -277,6 +277,13 @@ ScopedInterceptor::~ScopedInterceptor() {
# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver)
#endif
#define READ_STRING_OF_LEN(thr, pc, s, len, n) \
MemoryAccessRange((thr), (pc), (uptr)(s), \
common_flags()->strict_string_checks ? (len) + 1 : (n), false)
#define READ_STRING(thr, pc, s, n) \
READ_STRING_OF_LEN((thr), (pc), (s), internal_strlen(s), (n))
#define BLOCK_REAL(name) (BlockingCall(thr), REAL(name))
struct BlockingCall {
@ -708,8 +715,9 @@ TSAN_INTERCEPTOR(void*, memmove, void *dst, void *src, uptr n) {
TSAN_INTERCEPTOR(char*, strchr, char *s, int c) {
SCOPED_TSAN_INTERCEPTOR(strchr, s, c);
char *res = REAL(strchr)(s, c);
uptr len = res ? (char*)res - (char*)s + 1 : internal_strlen(s) + 1;
MemoryAccessRange(thr, pc, (uptr)s, len, false);
uptr len = internal_strlen(s);
uptr n = res ? (char*)res - (char*)s + 1 : len + 1;
READ_STRING_OF_LEN(thr, pc, s, len, n);
return res;
}
@ -717,7 +725,7 @@ TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) {
SCOPED_TSAN_INTERCEPTOR(strchrnul, s, c);
char *res = REAL(strchrnul)(s, c);
uptr len = (char*)res - (char*)s + 1;
MemoryAccessRange(thr, pc, (uptr)s, len, false);
READ_STRING(thr, pc, s, len);
return res;
}
@ -1398,6 +1406,7 @@ TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf);
READ_STRING(thr, pc, path, 0);
return REAL(__xstat)(version, path, buf);
}
#define TSAN_MAYBE_INTERCEPT___XSTAT TSAN_INTERCEPT(__xstat)
@ -1408,9 +1417,11 @@ TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
#if SANITIZER_FREEBSD
SCOPED_TSAN_INTERCEPTOR(stat, path, buf);
READ_STRING(thr, pc, path, 0);
return REAL(stat)(path, buf);
#else
SCOPED_TSAN_INTERCEPTOR(__xstat, 0, path, buf);
READ_STRING(thr, pc, path, 0);
return REAL(__xstat)(0, path, buf);
#endif
}
@ -1418,6 +1429,7 @@ TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf);
READ_STRING(thr, pc, path, 0);
return REAL(__xstat64)(version, path, buf);
}
#define TSAN_MAYBE_INTERCEPT___XSTAT64 TSAN_INTERCEPT(__xstat64)
@ -1428,6 +1440,7 @@ TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf);
READ_STRING(thr, pc, path, 0);
return REAL(__xstat64)(0, path, buf);
}
#define TSAN_MAYBE_INTERCEPT_STAT64 TSAN_INTERCEPT(stat64)
@ -1438,6 +1451,7 @@ TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf);
READ_STRING(thr, pc, path, 0);
return REAL(__lxstat)(version, path, buf);
}
#define TSAN_MAYBE_INTERCEPT___LXSTAT TSAN_INTERCEPT(__lxstat)
@ -1448,9 +1462,11 @@ TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
#if SANITIZER_FREEBSD
SCOPED_TSAN_INTERCEPTOR(lstat, path, buf);
READ_STRING(thr, pc, path, 0);
return REAL(lstat)(path, buf);
#else
SCOPED_TSAN_INTERCEPTOR(__lxstat, 0, path, buf);
READ_STRING(thr, pc, path, 0);
return REAL(__lxstat)(0, path, buf);
#endif
}
@ -1458,6 +1474,7 @@ TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf);
READ_STRING(thr, pc, path, 0);
return REAL(__lxstat64)(version, path, buf);
}
#define TSAN_MAYBE_INTERCEPT___LXSTAT64 TSAN_INTERCEPT(__lxstat64)
@ -1468,6 +1485,7 @@ TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf);
READ_STRING(thr, pc, path, 0);
return REAL(__lxstat64)(0, path, buf);
}
#define TSAN_MAYBE_INTERCEPT_LSTAT64 TSAN_INTERCEPT(lstat64)
@ -1527,6 +1545,7 @@ TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode);
READ_STRING(thr, pc, name, 0);
int fd = REAL(open)(name, flags, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
@ -1536,6 +1555,7 @@ TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode);
READ_STRING(thr, pc, name, 0);
int fd = REAL(open64)(name, flags, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
@ -1548,6 +1568,7 @@ TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat, name, mode);
READ_STRING(thr, pc, name, 0);
int fd = REAL(creat)(name, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
@ -1557,6 +1578,7 @@ TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat64, name, mode);
READ_STRING(thr, pc, name, 0);
int fd = REAL(creat64)(name, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);

View File

@ -0,0 +1,55 @@
// Test strict_string_checks option in atoi function
// RUN: %clang_asan %s -o %t
// RUN: %run %t test1 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test1 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1
// RUN: %run %t test2 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test2 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2
// RUN: %run %t test3 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test3 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test3 2>&1 | FileCheck %s --check-prefix=CHECK3
#include <assert.h>
#include <stdlib.h>
#include <string.h>
void test1(char *array) {
// Last symbol is non-digit
memset(array, '1', 10);
array[9] = 'a';
int r = atoi(array);
assert(r == 111111111);
}
void test2(char *array) {
// Single non-digit symbol
array[9] = 'a';
int r = atoi(array + 9);
assert(r == 0);
}
void test3(char *array) {
// Incorrect number format
memset(array, ' ', 10);
array[9] = '-';
array[8] = '-';
int r = atoi(array);
assert(r == 0);
}
int main(int argc, char **argv) {
char *array = (char*)malloc(10);
if (argc != 2) return 1;
if (!strcmp(argv[1], "test1")) test1(array);
// CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK1: READ of size 11
if (!strcmp(argv[1], "test2")) test2(array);
// CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK2: READ of size 2
if (!strcmp(argv[1], "test3")) test3(array);
// CHECK3: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK3: READ of size 11
free(array);
return 0;
}

View File

@ -0,0 +1,55 @@
// Test strict_string_checks option in atol function
// RUN: %clang_asan %s -o %t
// RUN: %run %t test1 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test1 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1
// RUN: %run %t test2 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test2 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2
// RUN: %run %t test3 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test3 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test3 2>&1 | FileCheck %s --check-prefix=CHECK3
#include <assert.h>
#include <stdlib.h>
#include <string.h>
void test1(char *array) {
// Last symbol is non-digit
memset(array, '1', 10);
array[9] = 'a';
long r = atol(array);
assert(r == 111111111);
}
void test2(char *array) {
// Single non-digit symbol
array[9] = 'a';
long r = atol(array + 9);
assert(r == 0);
}
void test3(char *array) {
// Incorrect number format
memset(array, ' ', 10);
array[9] = '-';
array[8] = '-';
long r = atol(array);
assert(r == 0);
}
int main(int argc, char **argv) {
char *array = (char*)malloc(10);
if (argc != 2) return 1;
if (!strcmp(argv[1], "test1")) test1(array);
// CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK1: READ of size 11
if (!strcmp(argv[1], "test2")) test2(array);
// CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK2: READ of size 2
if (!strcmp(argv[1], "test3")) test3(array);
// CHECK3: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK3: READ of size 11
free(array);
return 0;
}

View File

@ -0,0 +1,55 @@
// Test strict_string_checks option in atoll function
// RUN: %clang_asan %s -o %t
// RUN: %run %t test1 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test1 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1
// RUN: %run %t test2 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test2 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2
// RUN: %run %t test3 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test3 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test3 2>&1 | FileCheck %s --check-prefix=CHECK3
#include <assert.h>
#include <stdlib.h>
#include <string.h>
void test1(char *array) {
// Last symbol is non-digit
memset(array, '1', 10);
array[9] = 'a';
long long r = atoll(array);
assert(r == 111111111);
}
void test2(char *array) {
// Single non-digit symbol
array[9] = 'a';
long long r = atoll(array + 9);
assert(r == 0);
}
void test3(char *array) {
// Incorrect number format
memset(array, ' ', 10);
array[9] = '-';
array[8] = '-';
long long r = atoll(array);
assert(r == 0);
}
int main(int argc, char **argv) {
char *array = (char*)malloc(10);
if (argc != 2) return 1;
if (!strcmp(argv[1], "test1")) test1(array);
// CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK1: READ of size 11
if (!strcmp(argv[1], "test2")) test2(array);
// CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK2: READ of size 2
if (!strcmp(argv[1], "test3")) test3(array);
// CHECK3: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK3: READ of size 11
free(array);
return 0;
}

View File

@ -0,0 +1,44 @@
// Test strict_string_checks option in strcat function
// RUN: %clang_asan %s -o %t
// RUN: not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1-NONSTRICT --check-prefix=CHECK1
// RUN: ASAN_OPTIONS=strict_string_checks=false not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1-NONSTRICT --check-prefix=CHECK1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1-STRICT --check-prefix=CHECK1
// RUN: not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2-NONSTRICT --check-prefix=CHECK2
// RUN: ASAN_OPTIONS=strict_string_checks=false not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2-NONSTRICT --check-prefix=CHECK2
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2-STRICT --check-prefix=CHECK2
#include <assert.h>
#include <stdlib.h>
#include <string.h>
void test1(char *to, int to_size, char *from) {
// One of arguments points to not allocated memory.
char* r = strcat(to + to_size, from);
}
void test2(char *to, int to_size, char *from) {
// "to" is not zero-terminated.
memset(to, 'z', to_size);
char* r = strcat(to, from);
}
int main(int argc, char **argv) {
size_t to_size = 100;
char *to = (char*)malloc(to_size);
size_t from_size = 20;
char *from = (char*)malloc(from_size);
memset(from, 'z', from_size);
from[from_size - 1] = '\0';
if (argc != 2) return 1;
if (!strcmp(argv[1], "test1")) test1(to, to_size, from);
// CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK1-STRICT: READ of size 1
// CHECK1-NONSTRICT: WRITE of size 20
if (!strcmp(argv[1], "test2")) test2(to, to_size, from);
// CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK2-STRICT: READ of size 101
// CHECK2-NONSTRICT: WRITE of size 20
free(to);
free(from);
return 0;
}

View File

@ -0,0 +1,22 @@
// Test strict_string_checks option in strchr function
// RUN: %clang_asan %s -o %t && %run %t 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t 2>&1 | FileCheck %s
#include <assert.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
size_t size = 100;
char fill = 'o';
char *s = (char*)malloc(size);
memset(s, fill, size);
char c = 'o';
char* r = strchr(s, c);
// CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK: READ of size 101
assert(r == s);
free(s);
return 0;
}

View File

@ -0,0 +1,26 @@
// Test strict_string_checks option in strcmp function
// RUN: %clang_asan %s -o %t && %run %t 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t 2>&1 | FileCheck %s
#include <assert.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
size_t size = 100;
char fill = 'o';
char *s1 = (char*)malloc(size);
memset(s1, fill, size);
char *s2 = (char*)malloc(size);
memset(s2, fill, size);
s1[size - 1] = 'z';
s2[size - 1] = 'x';
int r = strcmp(s1, s2);
// CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK: READ of size 101
assert(r == 1);
free(s1);
free(s2);
return 0;
}

View File

@ -0,0 +1,44 @@
// Test strict_string_checks option in strncat function
// RUN: %clang_asan %s -o %t
// RUN: not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1-NONSTRICT --check-prefix=CHECK1
// RUN: ASAN_OPTIONS=strict_string_checks=false not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1-NONSTRICT --check-prefix=CHECK1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1-STRICT --check-prefix=CHECK1
// RUN: not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2-NONSTRICT --check-prefix=CHECK2
// RUN: ASAN_OPTIONS=strict_string_checks=false not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2-NONSTRICT --check-prefix=CHECK2
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2-STRICT --check-prefix=CHECK2
#include <assert.h>
#include <stdlib.h>
#include <string.h>
void test1(char *to, int to_size, char *from) {
// One of arguments points to not allocated memory.
char* r = strncat(to + to_size, from, 2);
}
void test2(char *to, int to_size, char *from) {
// "to" is not zero-terminated.
memset(to, 'z', to_size);
char* r = strncat(to, from, 1);
}
int main(int argc, char **argv) {
size_t to_size = 100;
char *to = (char*)malloc(to_size);
size_t from_size = 20;
char *from = (char*)malloc(from_size);
memset(from, 'z', from_size);
from[from_size - 1] = '\0';
if (argc != 2) return 1;
if (!strcmp(argv[1], "test1")) test1(to, to_size, from);
// CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK1-STRICT: READ of size 1
// CHECK1-NONSTRICT: WRITE of size 3
if (!strcmp(argv[1], "test2")) test2(to, to_size, from);
// CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK2-STRICT: READ of size 101
// CHECK2-NONSTRICT: WRITE of size 2
free(to);
free(from);
return 0;
}

View File

@ -0,0 +1,111 @@
// Test strict_string_checks option in strtol function
// RUN: %clang_asan -DTEST1 %s -o %t
// RUN: %run %t test1 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test1 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1
// RUN: %run %t test2 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test2 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2
// RUN: %run %t test3 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test3 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test3 2>&1 | FileCheck %s --check-prefix=CHECK3
// RUN: %run %t test4 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test4 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test4 2>&1 | FileCheck %s --check-prefix=CHECK4
// RUN: %run %t test5 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test5 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test5 2>&1 | FileCheck %s --check-prefix=CHECK5
// RUN: %run %t test6 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test6 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test6 2>&1 | FileCheck %s --check-prefix=CHECK6
// RUN: %run %t test7 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test7 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test7 2>&1 | FileCheck %s --check-prefix=CHECK7
#include <assert.h>
#include <stdlib.h>
#include <string.h>
void test1(char *array, char *endptr) {
// Buffer overflow if there is no terminating null (depends on base)
long r = strtol(array, &endptr, 3);
assert(array + 2 == endptr);
assert(r == 5);
}
void test2(char *array, char *endptr) {
// Buffer overflow if there is no terminating null (depends on base)
array[2] = 'z';
long r = strtol(array, &endptr, 35);
assert(array + 2 == endptr);
assert(r == 37);
}
void test3(char *array, char *endptr) {
// Buffer overflow if base is invalid.
long r = strtol(array - 1, NULL, -1);
assert(r == 0);
}
void test4(char *array, char *endptr) {
// Buffer overflow if base is invalid.
long r = strtol(array + 3, NULL, 1);
assert(r == 0);
}
void test5(char *array, char *endptr) {
// Overflow if no digits are found.
array[0] = ' ';
array[1] = '+';
array[2] = '-';
long r = strtol(array, NULL, 0);
assert(r == 0);
}
void test6(char *array, char *endptr) {
// Overflow if no digits are found.
array[0] = ' ';
array[1] = array[2] = 'z';
long r = strtol(array, &endptr, 0);
assert(array == endptr);
assert(r == 0);
}
void test7(char *array, char *endptr) {
// Overflow if no digits are found.
array[2] = 'z';
long r = strtol(array + 2, NULL, 0);
assert(r == 0);
}
int main(int argc, char **argv) {
char *array = (char*)malloc(3);
char *endptr = NULL;
array[0] = '1';
array[1] = '2';
array[2] = '3';
if (argc != 2) return 1;
if (!strcmp(argv[1], "test1")) test1(array, endptr);
// CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK1: READ of size 4
if (!strcmp(argv[1], "test2")) test2(array, endptr);
// CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK2: READ of size 4
if (!strcmp(argv[1], "test3")) test3(array, endptr);
// CHECK3: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK3: READ of size 5
if (!strcmp(argv[1], "test4")) test4(array, endptr);
// CHECK4: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK4: READ of size 1
if (!strcmp(argv[1], "test5")) test5(array, endptr);
// CHECK5: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK5: READ of size 4
if (!strcmp(argv[1], "test6")) test6(array, endptr);
// CHECK6: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK6: READ of size 4
if (!strcmp(argv[1], "test7")) test7(array, endptr);
// CHECK7: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK7: READ of size 2
free(array);
return 0;
}

View File

@ -0,0 +1,111 @@
// Test strict_string_checks option in strtoll function
// RUN: %clang_asan -DTEST1 %s -o %t
// RUN: %run %t test1 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test1 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1
// RUN: %run %t test2 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test2 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2
// RUN: %run %t test3 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test3 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test3 2>&1 | FileCheck %s --check-prefix=CHECK3
// RUN: %run %t test4 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test4 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test4 2>&1 | FileCheck %s --check-prefix=CHECK4
// RUN: %run %t test5 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test5 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test5 2>&1 | FileCheck %s --check-prefix=CHECK5
// RUN: %run %t test6 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test6 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test6 2>&1 | FileCheck %s --check-prefix=CHECK6
// RUN: %run %t test7 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=false %run %t test7 2>&1
// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t test7 2>&1 | FileCheck %s --check-prefix=CHECK7
#include <assert.h>
#include <stdlib.h>
#include <string.h>
void test1(char *array, char *endptr) {
// Buffer overflow if there is no terminating null (depends on base)
long long r = strtoll(array, &endptr, 3);
assert(array + 2 == endptr);
assert(r == 5);
}
void test2(char *array, char *endptr) {
// Buffer overflow if there is no terminating null (depends on base)
array[2] = 'z';
long long r = strtoll(array, &endptr, 35);
assert(array + 2 == endptr);
assert(r == 37);
}
void test3(char *array, char *endptr) {
// Buffer overflow if base is invalid.
long long r = strtoll(array - 1, NULL, -1);
assert(r == 0);
}
void test4(char *array, char *endptr) {
// Buffer overflow if base is invalid.
long long r = strtoll(array + 3, NULL, 1);
assert(r == 0);
}
void test5(char *array, char *endptr) {
// Overflow if no digits are found.
array[0] = ' ';
array[1] = '+';
array[2] = '-';
long long r = strtoll(array, NULL, 0);
assert(r == 0);
}
void test6(char *array, char *endptr) {
// Overflow if no digits are found.
array[0] = ' ';
array[1] = array[2] = 'z';
long long r = strtoll(array, &endptr, 0);
assert(array == endptr);
assert(r == 0);
}
void test7(char *array, char *endptr) {
// Overflow if no digits are found.
array[2] = 'z';
long long r = strtoll(array + 2, NULL, 0);
assert(r == 0);
}
int main(int argc, char **argv) {
char *array = (char*)malloc(3);
char *endptr = NULL;
array[0] = '1';
array[1] = '2';
array[2] = '3';
if (argc != 2) return 1;
if (!strcmp(argv[1], "test1")) test1(array, endptr);
// CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK1: READ of size 4
if (!strcmp(argv[1], "test2")) test2(array, endptr);
// CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK2: READ of size 4
if (!strcmp(argv[1], "test3")) test3(array, endptr);
// CHECK3: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK3: READ of size 5
if (!strcmp(argv[1], "test4")) test4(array, endptr);
// CHECK4: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK4: READ of size 1
if (!strcmp(argv[1], "test5")) test5(array, endptr);
// CHECK5: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK5: READ of size 4
if (!strcmp(argv[1], "test6")) test6(array, endptr);
// CHECK6: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK6: READ of size 4
if (!strcmp(argv[1], "test7")) test7(array, endptr);
// CHECK7: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK7: READ of size 2
free(array);
return 0;
}