Index: lib/asan/asan_interceptors.cc =================================================================== --- lib/asan/asan_interceptors.cc +++ lib/asan/asan_interceptors.cc @@ -75,6 +75,10 @@ #define ASAN_WRITE_RANGE(ctx, offset, size) \ ACCESS_MEMORY_RANGE(ctx, offset, size, true) +#define ASAN_READ_STRING(ctx, s, n) \ + ASAN_READ_RANGE((ctx), (s), \ + common_flags()->strict_string_checks ? internal_strlen(s) + 1 : (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. @@ -511,7 +515,7 @@ 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); + ASAN_READ_STRING(ctx, str, bytes_read); } return result; } @@ -540,7 +544,7 @@ 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(ctx, to, 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 @@ -562,7 +566,7 @@ 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(ctx, to, 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, @@ -664,23 +668,6 @@ } #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(nptr); - } - CHECK(*endptr >= nptr); -} - INTERCEPTOR(long, strtol, const char *nptr, // NOLINT char **endptr, int base) { void *ctx; @@ -691,13 +678,7 @@ } 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; } @@ -718,7 +699,7 @@ // 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; } @@ -735,7 +716,7 @@ 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; } @@ -750,16 +731,7 @@ } 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; } @@ -773,7 +745,7 @@ 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 Index: lib/msan/msan_interceptors.cc =================================================================== --- lib/msan/msan_interceptors.cc +++ lib/msan/msan_interceptors.cc @@ -93,6 +93,10 @@ if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \ } while (0); +#define CHECK_UNPOISONED_STRING(x, n) \ + CHECK_UNPOISONED((x), \ + common_flags()->strict_string_checks ? internal_strlen(x) + 1 : (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); @@ -117,6 +121,7 @@ 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); @@ -282,13 +287,11 @@ 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 CopyPoison(dest, src, n + 1, &stack); return res; @@ -310,6 +313,7 @@ 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 CopyPoison(dest, src, n + 1, &stack); return res; @@ -321,6 +325,7 @@ // 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); CopyPoison(res, src, n + 1, &stack); return res; @@ -331,6 +336,7 @@ ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); + CHECK_UNPOISONED_STRING(src + n, 0); char *res = REAL(__strdup)(src); CopyPoison(res, src, n + 1, &stack); return res; @@ -380,6 +386,8 @@ 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 CopyPoison(dest + dest_size, src, src_size + 1, &stack); return res; @@ -390,6 +398,7 @@ 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 CopyPoison(dest + dest_size, src, copy_size, &stack); __msan_unpoison(dest + dest_size + copy_size, 1); // \0 @@ -665,6 +674,7 @@ 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; Index: lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- lib/sanitizer_common/sanitizer_common_interceptors.inc +++ lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -99,6 +99,10 @@ #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0) #endif +#define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n) \ + COMMON_INTERCEPTOR_READ_RANGE(ctx, s, \ + common_flags()->strict_string_checks ? internal_strlen(s) + 1 : n ) + struct FileMetadata { // For open_memstream(). char **addr; @@ -152,7 +156,8 @@ 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); } @@ -178,8 +183,8 @@ 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); } @@ -224,8 +229,8 @@ 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); } @@ -720,12 +725,13 @@ // 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); + COMMON_INTERCEPTOR_READ_STRING(ctx, format, 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; } @@ -1492,6 +1498,7 @@ __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, @@ -1522,6 +1529,7 @@ __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, @@ -1668,6 +1676,7 @@ 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 @@ -2338,6 +2347,37 @@ #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(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; @@ -2345,8 +2385,9 @@ // 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; } @@ -2356,8 +2397,9 @@ // 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; } @@ -3608,6 +3650,7 @@ 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); } @@ -4665,6 +4708,7 @@ 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); void *res = REAL(dlopen)(filename, flag); COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res); return res; Index: lib/sanitizer_common/sanitizer_flags.inc =================================================================== --- lib/sanitizer_common/sanitizer_flags.inc +++ lib/sanitizer_common/sanitizer_flags.inc @@ -141,3 +141,5 @@ "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, true, + "If set check that string arguments are properly null-terminated") Index: lib/tsan/rtl/tsan_interceptors.cc =================================================================== --- lib/tsan/rtl/tsan_interceptors.cc +++ lib/tsan/rtl/tsan_interceptors.cc @@ -240,6 +240,10 @@ # define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver) #endif +#define TSAN_READ_STRING(thr, pc, s, n) \ + MemoryAccessRange(thr, pc, (uptr)s, \ + common_flags()->strict_string_checks ? internal_strlen(s) + 1 : n, false) + #define BLOCK_REAL(name) (BlockingCall(thr), REAL(name)) struct BlockingCall { @@ -666,7 +670,7 @@ 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); + TSAN_READ_STRING(thr, pc, s, len); return res; } @@ -674,7 +678,7 @@ 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); + TSAN_READ_STRING(thr, pc, s, len); return res; } @@ -1351,6 +1355,7 @@ #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf); + TSAN_READ_STRING(thr, pc, path, 0); return REAL(__xstat)(version, path, buf); } #define TSAN_MAYBE_INTERCEPT___XSTAT TSAN_INTERCEPT(__xstat) @@ -1361,9 +1366,11 @@ TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) { #if SANITIZER_FREEBSD SCOPED_TSAN_INTERCEPTOR(stat, path, buf); + TSAN_READ_STRING(thr, pc, path, 0); return REAL(stat)(path, buf); #else SCOPED_TSAN_INTERCEPTOR(__xstat, 0, path, buf); + TSAN_READ_STRING(thr, pc, path, 0); return REAL(__xstat)(0, path, buf); #endif } @@ -1371,6 +1378,7 @@ #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf); + TSAN_READ_STRING(thr, pc, path, 0); return REAL(__xstat64)(version, path, buf); } #define TSAN_MAYBE_INTERCEPT___XSTAT64 TSAN_INTERCEPT(__xstat64) @@ -1381,6 +1389,7 @@ #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf); + TSAN_READ_STRING(thr, pc, path, 0); return REAL(__xstat64)(0, path, buf); } #define TSAN_MAYBE_INTERCEPT_STAT64 TSAN_INTERCEPT(stat64) @@ -1391,6 +1400,7 @@ #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf); + TSAN_READ_STRING(thr, pc, path, 0); return REAL(__lxstat)(version, path, buf); } #define TSAN_MAYBE_INTERCEPT___LXSTAT TSAN_INTERCEPT(__lxstat) @@ -1401,9 +1411,11 @@ TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) { #if SANITIZER_FREEBSD SCOPED_TSAN_INTERCEPTOR(lstat, path, buf); + TSAN_READ_STRING(thr, pc, path, 0); return REAL(lstat)(path, buf); #else SCOPED_TSAN_INTERCEPTOR(__lxstat, 0, path, buf); + TSAN_READ_STRING(thr, pc, path, 0); return REAL(__lxstat)(0, path, buf); #endif } @@ -1411,6 +1423,7 @@ #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf); + TSAN_READ_STRING(thr, pc, path, 0); return REAL(__lxstat64)(version, path, buf); } #define TSAN_MAYBE_INTERCEPT___LXSTAT64 TSAN_INTERCEPT(__lxstat64) @@ -1421,6 +1434,7 @@ #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf); + TSAN_READ_STRING(thr, pc, path, 0); return REAL(__lxstat64)(0, path, buf); } #define TSAN_MAYBE_INTERCEPT_LSTAT64 TSAN_INTERCEPT(lstat64) @@ -1480,6 +1494,7 @@ TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) { SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode); + TSAN_READ_STRING(thr, pc, name, 0); int fd = REAL(open)(name, flags, mode); if (fd >= 0) FdFileCreate(thr, pc, fd); @@ -1489,6 +1504,7 @@ #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) { SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode); + TSAN_READ_STRING(thr, pc, name, 0); int fd = REAL(open64)(name, flags, mode); if (fd >= 0) FdFileCreate(thr, pc, fd); @@ -1501,6 +1517,7 @@ TSAN_INTERCEPTOR(int, creat, const char *name, int mode) { SCOPED_TSAN_INTERCEPTOR(creat, name, mode); + TSAN_READ_STRING(thr, pc, name, 0); int fd = REAL(creat)(name, mode); if (fd >= 0) FdFileCreate(thr, pc, fd); @@ -1510,6 +1527,7 @@ #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) { SCOPED_TSAN_INTERCEPTOR(creat64, name, mode); + TSAN_READ_STRING(thr, pc, name, 0); int fd = REAL(creat64)(name, mode); if (fd >= 0) FdFileCreate(thr, pc, fd); Index: test/asan/TestCases/atoi_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/atoi_strict.c @@ -0,0 +1,17 @@ +// Test strict_string_checks option in atoi function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// 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 +#include + +int main(int argc, char **argv) { + char p[] = {'1', 'b'}; + char z = 0; + int r = atoi(p); + // CHECK: READ of size + // CHECK: '{{[p|z]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 1); + return 0; +} Index: test/asan/TestCases/atol_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/atol_strict.c @@ -0,0 +1,17 @@ +// Test strict_string_checks option in atol function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// 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 +#include + +int main(int argc, char **argv) { + char p[] = {'1', 'b'}; + char z = 0; + long r = atol(p); + // CHECK: READ of size + // CHECK: '{{[p|z]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 1); + return 0; +} Index: test/asan/TestCases/atoll_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/atoll_strict.c @@ -0,0 +1,17 @@ +// Test strict_string_checks option in atoll function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// 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 +#include + +int main(int argc, char **argv) { + char p[] = {'1', 'b'}; + char z = 0; + long long r = atoll(p); + // CHECK: READ of size + // CHECK: '{{[p|z]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 1); + return 0; +} Index: test/asan/TestCases/strcat_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strcat_strict.c @@ -0,0 +1,18 @@ +// Test strict_string_checks option in strcat function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-STRICT --check-prefix=CHECK +// RUN: ASAN_OPTIONS=strict_string_checks=false not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK +// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-STRICT --check-prefix=CHECK + +#include +#include + +int main(int argc, char **argv) { + char *r = 0; + char s2[] = {"a"}; + char s1[] = {'b'}; + char s3[] = {'\0', 'z', 'z'}; + r = strcat(s1, s2); + // CHECK-STRICT: READ of size + // CHECK: '{{s[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + return 0; +} Index: test/asan/TestCases/strchr_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strchr_strict.c @@ -0,0 +1,18 @@ +// Test strict_string_checks option in strchr function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// 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 +#include + +int main(int argc, char **argv) { + char *r = 0; + char c = 'a'; + char s[] = {'a', 'b', 'c'}; + char z = 0; + r = strchr(s, c); + // CHECK: '{{[s|z]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == s); + return 0; +} Index: test/asan/TestCases/strncat_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strncat_strict.c @@ -0,0 +1,18 @@ +// Test strict_string_checks option in strncat function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-STRICT --check-prefix=CHECK +// RUN: ASAN_OPTIONS=strict_string_checks=false not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK +// RUN: ASAN_OPTIONS=strict_string_checks=true not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-STRICT --check-prefix=CHECK + +#include +#include + +int main(int argc, char **argv) { + char *r = 0; + char s2[] = {"a"}; + char s1[] = {'b'}; + char s3[] = {'\0', 'z', 'z'}; + r = strncat(s1, s2, 1); + // CHECK-STRICT: READ of size + // CHECK: '{{s[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + return 0; +} Index: test/asan/TestCases/strtol_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strtol_strict.c @@ -0,0 +1,18 @@ +// Test strict_string_checks option in strtol function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// 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 +#include + +int main(int argc, char **argv) { + char *endptr; + char p[] = {'1', 'b'}; + char z = 0; + long r = strtol(p, &endptr, 2); + // CHECK: READ of size + // CHECK: '{{[p|z]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 1); + return 0; +} Index: test/asan/TestCases/strtoll_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strtoll_strict.c @@ -0,0 +1,18 @@ +// Test strict_string_checks option in strtoll function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// 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 +#include + +int main(int argc, char **argv) { + char *endptr; + char p[] = {'1', 'b'}; + char z = 0; + long long r = strtol(p, &endptr, 2); + // CHECK: READ of size + // CHECK: '{{[p|z]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 1); + return 0; +} Index: test/asan/Unit/lit.site.cfg.in =================================================================== --- test/asan/Unit/lit.site.cfg.in +++ test/asan/Unit/lit.site.cfg.in @@ -28,3 +28,6 @@ # Set LD_LIBRARY_PATH to pick dynamic runtime up properly. push_ld_library_path(config, config.compiler_rt_libdir) + +# Disable strict str checks in unit tests +config.environment['ASAN_OPTIONS'] = 'strict_string_checks=false'