Index: lib/asan/asan_interceptors.cc =================================================================== --- lib/asan/asan_interceptors.cc +++ lib/asan/asan_interceptors.cc @@ -75,6 +75,13 @@ #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 @@ 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 @@ 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 @@ 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 @@ } #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; @@ -656,13 +647,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; } @@ -683,7 +668,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; } @@ -700,7 +685,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; } @@ -715,16 +700,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; } @@ -738,7 +714,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/asan/tests/asan_str_test.cc =================================================================== --- lib/asan/tests/asan_str_test.cc +++ lib/asan/tests/asan_str_test.cc @@ -290,9 +290,6 @@ 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 @@ // 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 @@ // 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 @@ 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 @@ 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 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) { char *array = MallocAndMemsetString(3); - char *endptr = NULL; array[0] = '1'; array[1] = '2'; array[2] = '3'; @@ -554,19 +539,12 @@ 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 @@ 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); } Index: lib/msan/msan_interceptors.cc =================================================================== --- lib/msan/msan_interceptors.cc +++ lib/msan/msan_interceptors.cc @@ -94,6 +94,13 @@ 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(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 @@ 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 @@ 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 @@ // 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ -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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ __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 @@ __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(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 @@ #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; @@ -2366,8 +2408,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; } @@ -2377,8 +2420,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; } @@ -3642,6 +3686,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); } @@ -4699,6 +4744,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); COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag); void *res = REAL(dlopen)(filename, flag); COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res); Index: lib/sanitizer_common/sanitizer_flags.inc =================================================================== --- lib/sanitizer_common/sanitizer_flags.inc +++ lib/sanitizer_common/sanitizer_flags.inc @@ -151,3 +151,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, false, + "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 @@ -277,6 +277,13 @@ # 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(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 @@ 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 @@ #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, 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 @@ #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 @@ #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 @@ #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, 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 @@ #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 @@ #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, 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 @@ #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, 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 @@ #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); Index: test/asan/TestCases/atoi_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/atoi_strict.c @@ -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 +#include +#include + +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; +} Index: test/asan/TestCases/atol_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/atol_strict.c @@ -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 +#include +#include + +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; +} Index: test/asan/TestCases/atoll_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/atoll_strict.c @@ -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 +#include +#include + +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; +} Index: test/asan/TestCases/strcat_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strcat_strict.c @@ -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 +#include +#include + +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; +} Index: test/asan/TestCases/strchr_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strchr_strict.c @@ -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 +#include +#include + +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; +} Index: test/asan/TestCases/strcmp_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strcmp_strict.c @@ -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 +#include +#include + +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; +} Index: test/asan/TestCases/strncat_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strncat_strict.c @@ -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 +#include +#include + +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; +} Index: test/asan/TestCases/strtol_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strtol_strict.c @@ -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 +#include +#include + +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; +} Index: test/asan/TestCases/strtoll_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strtoll_strict.c @@ -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 +#include +#include + +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; +}