mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-18 13:31:01 +00:00
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:
parent
9d3949cca3
commit
1e5b9f4131
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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")
|
||||
|
@ -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);
|
||||
|
55
compiler-rt/test/asan/TestCases/atoi_strict.c
Normal file
55
compiler-rt/test/asan/TestCases/atoi_strict.c
Normal 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;
|
||||
}
|
55
compiler-rt/test/asan/TestCases/atol_strict.c
Normal file
55
compiler-rt/test/asan/TestCases/atol_strict.c
Normal 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;
|
||||
}
|
55
compiler-rt/test/asan/TestCases/atoll_strict.c
Normal file
55
compiler-rt/test/asan/TestCases/atoll_strict.c
Normal 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;
|
||||
}
|
44
compiler-rt/test/asan/TestCases/strcat_strict.c
Normal file
44
compiler-rt/test/asan/TestCases/strcat_strict.c
Normal 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;
|
||||
}
|
22
compiler-rt/test/asan/TestCases/strchr_strict.c
Normal file
22
compiler-rt/test/asan/TestCases/strchr_strict.c
Normal 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;
|
||||
}
|
26
compiler-rt/test/asan/TestCases/strcmp_strict.c
Normal file
26
compiler-rt/test/asan/TestCases/strcmp_strict.c
Normal 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;
|
||||
}
|
44
compiler-rt/test/asan/TestCases/strncat_strict.c
Normal file
44
compiler-rt/test/asan/TestCases/strncat_strict.c
Normal 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;
|
||||
}
|
111
compiler-rt/test/asan/TestCases/strtol_strict.c
Normal file
111
compiler-rt/test/asan/TestCases/strtol_strict.c
Normal 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;
|
||||
}
|
111
compiler-rt/test/asan/TestCases/strtoll_strict.c
Normal file
111
compiler-rt/test/asan/TestCases/strtoll_strict.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user