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_str ? 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. @@ -498,7 +502,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; } @@ -527,7 +531,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 @@ -549,7 +553,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, @@ -651,23 +655,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; @@ -678,13 +665,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; } @@ -705,7 +686,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; } @@ -722,7 +703,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; } @@ -737,16 +718,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; } @@ -760,7 +732,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/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -126,6 +126,12 @@ ParseFlag(str, &f->replace_str, "replace_str", "If set, uses custom wrappers and replacements for libc string functions " "to find more errors."); + // The replace_str=false overwrites other replace_str* flags + if (!f->replace_str) { + cf->replace_strstr = false; + cf->replace_strcspn = false; + cf->replace_strpbrk = false; + } ParseFlag(str, &f->replace_intrin, "replace_intrin", "If set, uses custom wrappers for memset/memcpy/memmove intinsics."); 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_str ? 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; @@ -319,6 +323,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; @@ -329,6 +334,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; @@ -376,6 +382,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; @@ -386,6 +394,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 @@ -661,6 +670,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 @@ -98,6 +98,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_str ? internal_strlen(s) + 1 : n ) + struct FileMetadata { // For open_memstream(). char **addr; @@ -151,7 +155,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); } @@ -177,8 +182,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); } @@ -223,8 +228,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); } @@ -250,6 +255,87 @@ #define INIT_STRNCASECMP #endif + +#if SANITIZER_INTERCEPT_STRSTR + +static inline void StrstrCheck(void *ctx, char *r, const char *s1, + const char *s2 ) { + uptr len1 = REAL(strlen)(s1) + 1; + uptr len2 = REAL(strlen)(s2) + 1; + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r ? r - s1 + len2 : len1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s2, r ? len2 : + (len2 <= len1 ? len2 - 1 : len1)); +} + +INTERCEPTOR(char*, strstr, const char *s1, const char *s2) { + if COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED + return internal_strstr(s1, s2); + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strstr, s1, s2); + char *r = REAL(strstr)(s1, s2); + if (common_flags()->replace_strstr) { + StrstrCheck(ctx, r, s1, s2); + } + return r; +} + +#define INIT_STRSTR COMMON_INTERCEPT_FUNCTION(strstr); + +INTERCEPTOR(char*, strcasestr, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strcasestr, s1, s2); + char *r = REAL(strcasestr)(s1, s2); + if (common_flags()->replace_strstr) { + StrstrCheck(ctx, r, s1, s2); + } + return r; +} + +#define INIT_STRCASESTR COMMON_INTERCEPT_FUNCTION(strcasestr); + +#else +#define INIT_STRSTR +#define INIT_STRCASESTR +#endif + + +#if SANITIZER_INTERCEPT_STRCSPN + +INTERCEPTOR(SIZE_T, strcspn, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strcspn, s1, s2); + SIZE_T r = REAL(strcspn)(s1, s2); + if (common_flags()->replace_strcspn) { + COMMON_INTERCEPTOR_READ_STRING(ctx, s2, + s1 && *s1 ? internal_strchr(s2, s1[r]) - s2 + 1 : 0); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1); + } + return r; +} + +#define INIT_STRCSPN COMMON_INTERCEPT_FUNCTION(strcspn); + +INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strpbrk, s1, s2); + char *r = REAL(strpbrk)(s1, s2); + if (common_flags()->replace_strpbrk) { + COMMON_INTERCEPTOR_READ_STRING(ctx, s2, + r ? internal_strchr(s2, *r) - s2 + 1 : + *s1 ? REAL(strlen)(s2) + 1 : 0); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, + r ? r - s1 + 1: REAL(strlen)(s1) + 1); + } + return r; +} + +#define INIT_STRPBRK COMMON_INTERCEPT_FUNCTION(strpbrk); + +#else +#define INIT_STRCSPN +#define INIT_STRPBRK +#endif + #if SANITIZER_INTERCEPT_MEMCHR INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) { void *ctx; @@ -719,12 +805,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; } @@ -1473,6 +1560,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, @@ -1503,6 +1591,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, @@ -1649,6 +1738,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 @@ -2308,6 +2398,40 @@ #define INIT_GET_CURRENT_DIR_NAME #endif +UNUSED static inline bool IsValidStrtolBase(int base) { + return (base == 0) || (2 <= base && base <= 36); +} + +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. + if (IsValidStrtolBase(base)) { + FixRealStrtolEndptr(nptr, &real_endptr); + } + COMMON_INTERCEPTOR_READ_STRING(ctx, nptr, IsValidStrtolBase(base) ? + (real_endptr - nptr) + 1 : 0); +} + + #if SANITIZER_INTERCEPT_STRTOIMAX INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) { void *ctx; @@ -2315,8 +2439,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; } @@ -2326,8 +2451,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; } @@ -3578,6 +3704,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); } @@ -4629,6 +4756,7 @@ INTERCEPTOR(void*, dlopen, const char *filename, int flag) { void *ctx; COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag); + COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0); void *res = REAL(dlopen)(filename, flag); COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res); return res; @@ -4742,6 +4870,10 @@ INIT_STRNCMP; INIT_STRCASECMP; INIT_STRNCASECMP; + INIT_STRSTR; + INIT_STRCASESTR; + INIT_STRCSPN; + INIT_STRPBRK; INIT_MEMCHR; INIT_MEMRCHR; INIT_READ; Index: lib/sanitizer_common/sanitizer_flags.h =================================================================== --- lib/sanitizer_common/sanitizer_flags.h +++ lib/sanitizer_common/sanitizer_flags.h @@ -61,6 +61,10 @@ const char *suppressions; bool print_suppressions; bool disable_coredump; + bool replace_strstr; + bool replace_strcspn; + bool replace_strpbrk; + bool strict_str; bool symbolize_inline_frames; const char *stack_trace_format; }; Index: lib/sanitizer_common/sanitizer_flags.cc =================================================================== --- lib/sanitizer_common/sanitizer_flags.cc +++ lib/sanitizer_common/sanitizer_flags.cc @@ -67,6 +67,10 @@ f->suppressions = ""; f->print_suppressions = true; f->disable_coredump = (SANITIZER_WORDSIZE == 64); + f->replace_strstr = true; + f->replace_strcspn = true; + f->replace_strpbrk = true; + f->strict_str = true; f->symbolize_inline_frames = true; f->stack_trace_format = "DEFAULT"; } @@ -160,6 +164,14 @@ "Disable core dumping. By default, disable_core=1 on 64-bit to avoid " "dumping a 16T+ core file. Ignored on OSes that don't dump core by" "default and for sanitizers that don't reserve lots of virtual memory."); + ParseFlag(str, &f->replace_strstr, "replace_strstr", "If set, uses custom " + "wrappers for strstr and strcasestr functions to find more errors."); + ParseFlag(str, &f->replace_strcspn, "replace_strcspn", "If set, uses custom " + "wrappers for strcspn function to find more errors."); + ParseFlag(str, &f->replace_strpbrk, "replace_strpbrk", "If set, uses custom " + "wrappers for strpbrk function to find more errors."); + ParseFlag(str, &f->strict_str, "strict_str", "If set, check that " + "string arguments are properly null-terminated"); ParseFlag(str, &f->symbolize_inline_frames, "symbolize_inline_frames", "Print inlined frames in stacktraces. Defaults to true."); ParseFlag(str, &f->stack_trace_format, "stack_trace_format", Index: lib/sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- lib/sanitizer_common/sanitizer_platform_interceptors.h +++ lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -54,6 +54,10 @@ #endif #define SANITIZER_INTERCEPT_STRCMP 1 +#define SANITIZER_INTERCEPT_STRSTR 1 +#define SANITIZER_INTERCEPT_STRCASESTR SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_STRCSPN 1 +#define SANITIZER_INTERCEPT_STRPBRK 1 #define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MEMCHR 1 Index: lib/tsan/rtl/tsan_interceptors.cc =================================================================== --- lib/tsan/rtl/tsan_interceptors.cc +++ lib/tsan/rtl/tsan_interceptors.cc @@ -238,6 +238,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_str ? internal_strlen(s) + 1 : n, false ) + #define BLOCK_REAL(name) (BlockingCall(thr), REAL(name)) struct BlockingCall { @@ -664,7 +668,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; } @@ -672,7 +676,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; } @@ -698,16 +702,6 @@ return REAL(strncpy)(dst, src, n); } -TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) { - SCOPED_TSAN_INTERCEPTOR(strstr, s1, s2); - const char *res = REAL(strstr)(s1, s2); - uptr len1 = internal_strlen(s1); - uptr len2 = internal_strlen(s2); - MemoryAccessRange(thr, pc, (uptr)s1, len1 + 1, false); - MemoryAccessRange(thr, pc, (uptr)s2, len2 + 1, false); - return res; -} - TSAN_INTERCEPTOR(char*, strdup, const char *str) { SCOPED_TSAN_INTERCEPTOR(strdup, str); // strdup will call malloc, so no instrumentation is required here. @@ -1347,6 +1341,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) @@ -1357,9 +1352,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 } @@ -1367,6 +1364,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) @@ -1377,6 +1375,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) @@ -1387,6 +1386,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) @@ -1397,9 +1397,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 } @@ -1407,6 +1409,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) @@ -1417,6 +1420,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) @@ -1476,6 +1480,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); @@ -1485,6 +1490,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); @@ -1497,6 +1503,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); @@ -1506,6 +1513,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); @@ -2429,7 +2437,6 @@ TSAN_INTERCEPT(strrchr); TSAN_INTERCEPT(strcpy); // NOLINT TSAN_INTERCEPT(strncpy); - TSAN_INTERCEPT(strstr); TSAN_INTERCEPT(strdup); TSAN_INTERCEPT(pthread_create); Index: test/asan/TestCases/atoi_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/atoi_strict.c @@ -0,0 +1,17 @@ +// Test strict_str option in atoi function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=strict_str=false %run %t 2>&1 +// RUN: ASAN_OPTIONS=strict_str=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_str option in atol function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=strict_str=false %run %t 2>&1 +// RUN: ASAN_OPTIONS=strict_str=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_str option in atoll function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=strict_str=false %run %t 2>&1 +// RUN: ASAN_OPTIONS=strict_str=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/strcasestr-1.c =================================================================== --- /dev/null +++ test/asan/TestCases/strcasestr-1.c @@ -0,0 +1,23 @@ +// Test haystack overflow in strcasestr function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Test replace_strstr asan option +// RUN: ASAN_OPTIONS=replace_strstr=false %run %t 2>&1 + +// There's no interceptor for strcasestr on Windows +// XFAIL: win32 + +#define _GNU_SOURCE +#include +#include + +int main(int argc, char **argv) { + char *r = 0; + char s2[] = "c"; + char s1[] = {'a', 'b'}; + char s3 = 0; + r = strcasestr(s1, s2); + // CHECK:'s{{[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 0); + return 0; +} Index: test/asan/TestCases/strcasestr-2.c =================================================================== --- /dev/null +++ test/asan/TestCases/strcasestr-2.c @@ -0,0 +1,23 @@ +// Test needle overflow in strcasestr function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Test replace_strstr asan option +// RUN: ASAN_OPTIONS=replace_strstr=false %run %t 2>&1 + +// There's no interceptor for strcasestr on Windows +// XFAIL: win32 + +#define _GNU_SOURCE +#include +#include + +int main(int argc, char **argv) { + char *r = 0; + char s1[] = "ab"; + char s2[] = {'c'}; + char s3 = 0; + r = strcasestr(s1, s2); + assert(r == 0); + // CHECK:'s{{[2|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + return 0; +} Index: test/asan/TestCases/strcasestr_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strcasestr_strict.c @@ -0,0 +1,23 @@ +// Test strict_str option in strcasestr function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=strict_str=false %run %t 2>&1 +// RUN: ASAN_OPTIONS=strict_str=true not %run %t 2>&1 | FileCheck %s + +// There's no interceptor for strcasestr on Windows +// XFAIL: win32 + +#define _GNU_SOURCE +#include +#include + +int main(int argc, char **argv) { + char *r = 0; + char s2[] = {"Ab"}; + char s1[] = {'a', 'b', 'c'}; + char s3 = 0; + r = strcasestr(s1, s2); + // CHECK: READ of size + // CHECK:'{{s[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == s1); + return 0; +} Index: test/asan/TestCases/strcat_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strcat_strict.c @@ -0,0 +1,18 @@ +// Test strict_str 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_str=false not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK +// RUN: ASAN_OPTIONS=strict_str=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_str option in strchr function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=strict_str=false %run %t 2>&1 +// RUN: ASAN_OPTIONS=strict_str=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/strcspn-1.c =================================================================== --- /dev/null +++ test/asan/TestCases/strcspn-1.c @@ -0,0 +1,19 @@ +// Test string s1 overflow in strcspn function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Test replace_strcspn asan option +// RUN: ASAN_OPTIONS=replace_strcspn=false %run %t 2>&1 + +#include +#include + +int main(int argc, char **argv) { + size_t r; + char s2[] = "ab"; + char s1[] = {'c', 'd'}; + char s3 = 0; + r = strcspn(s1, s2); + // CHECK:'s{{[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r >= sizeof(s1)); + return 0; +} Index: test/asan/TestCases/strcspn-2.c =================================================================== --- /dev/null +++ test/asan/TestCases/strcspn-2.c @@ -0,0 +1,19 @@ +// Test stopset overflow in strcspn function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Test replace_strcspn asan option +// RUN: ASAN_OPTIONS=replace_strcspn=false %run %t 2>&1 + +#include +#include + +int main(int argc, char **argv) { + size_t r; + char s1[] = "ab"; + char s2[] = {'c', 'd'}; + char s3 = 0; + r = strcspn(s1, s2); + // CHECK:'s{{[2|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == sizeof(s1) - 1); + return 0; +} Index: test/asan/TestCases/strcspn_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strcspn_strict.c @@ -0,0 +1,19 @@ +// Test strict_str option in strcspn function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=strict_str=false %run %t 2>&1 +// RUN: ASAN_OPTIONS=strict_str=true not %run %t 2>&1 | FileCheck %s + +#include +#include + +int main(int argc, char **argv) { + size_t r; + char s2[] = "ab"; + char s1[] = {'c', 'a'}; + char s3 = 0; + r = strcspn(s1, s2); + // CHECK: READ of size + // CHECK:'{{s[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 1); + return 0; +} Index: test/asan/TestCases/strncat_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strncat_strict.c @@ -0,0 +1,18 @@ +// Test strict_str 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_str=false not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK +// RUN: ASAN_OPTIONS=strict_str=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/strpbrk-1.c =================================================================== --- /dev/null +++ test/asan/TestCases/strpbrk-1.c @@ -0,0 +1,19 @@ +// Test string s1 overflow in strpbrk function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Test replace_strpbrk asan option +// RUN: ASAN_OPTIONS=replace_strpbrk=false %run %t 2>&1 + +#include +#include + +int main(int argc, char **argv) { + char *r; + char s2[] = "ab"; + char s1[] = {'c', 'd'}; + char s3[] = "a"; + r = strpbrk(s1, s2); + // CHECK:'s{{[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r <= s3); + return 0; +} Index: test/asan/TestCases/strpbrk-2.c =================================================================== --- /dev/null +++ test/asan/TestCases/strpbrk-2.c @@ -0,0 +1,19 @@ +// Test stopset overflow in strpbrk function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Test replace_strpbrk asan option +// RUN: ASAN_OPTIONS=replace_strpbrk=false %run %t 2>&1 + +#include +#include + +int main(int argc, char **argv) { + char *r; + char s1[] = "a"; + char s2[] = {'b', 'c'}; + char s3 = 0; + r = strpbrk(s1, s2); + // CHECK:'s{{[2|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == (r ? s1 : 0)); + return 0; +} Index: test/asan/TestCases/strpbrk_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strpbrk_strict.c @@ -0,0 +1,19 @@ +// Test strict_str option in strpbrk function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=strict_str=false %run %t 2>&1 +// RUN: ASAN_OPTIONS=strict_str=true not %run %t 2>&1 | FileCheck %s + +#include +#include + +int main(int argc, char **argv) { + char *r; + char s2[] = "ab"; + char s1[] = {'c', 'a'}; + char s3 = 0; + r = strpbrk(s1, s2); + // CHECK: READ of size + // CHECK:'{{s[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == s1 + 1); + return 0; +} Index: test/asan/TestCases/strstr-1.c =================================================================== --- /dev/null +++ test/asan/TestCases/strstr-1.c @@ -0,0 +1,19 @@ +// Test haystack overflow in strstr function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Test replace_strstr asan option +// RUN: ASAN_OPTIONS=replace_strstr=false %run %t 2>&1 + +#include +#include + +int main(int argc, char **argv) { + char *r = 0; + char s2[] = "c"; + char s1[] = {'a', 'b'}; + char s3 = 0; + r = strstr(s1, s2); + // CHECK:'s{{[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 0); + return 0; +} Index: test/asan/TestCases/strstr-2.c =================================================================== --- /dev/null +++ test/asan/TestCases/strstr-2.c @@ -0,0 +1,19 @@ +// Test needle overflow in strstr function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Test replace_strstr asan option +// RUN: ASAN_OPTIONS=replace_strstr=false %run %t 2>&1 + +#include +#include + +int main(int argc, char **argv) { + char *r = 0; + char s1[] = "ab"; + char s2[] = {'c'}; + char s3 = 0; + r = strstr(s1, s2); + // CHECK:'s{{[2|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 0); + return 0; +} Index: test/asan/TestCases/strstr_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strstr_strict.c @@ -0,0 +1,19 @@ +// Test strict_str option in strstr function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=strict_str=false %run %t 2>&1 +// RUN: ASAN_OPTIONS=strict_str=true not %run %t 2>&1 | FileCheck %s + +#include +#include + +int main(int argc, char **argv) { + char *r = 0; + char s2[] = {"ab"}; + char s1[] = {'a', 'b', 'c'}; + char s3 = 0; + r = strstr(s1, s2); + // CHECK: READ of size + // CHECK:'{{s[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == s1); + return 0; +} Index: test/asan/TestCases/strtol_strict.c =================================================================== --- /dev/null +++ test/asan/TestCases/strtol_strict.c @@ -0,0 +1,18 @@ +// Test strict_str option in strtol function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=strict_str=false %run %t 2>&1 +// RUN: ASAN_OPTIONS=strict_str=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_str option in strtoll function +// RUN: %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=strict_str=false %run %t 2>&1 +// RUN: ASAN_OPTIONS=strict_str=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 @@ -22,3 +22,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_str=false' Index: test/sanitizer_common/TestCases/strcasestr.c =================================================================== --- /dev/null +++ test/sanitizer_common/TestCases/strcasestr.c @@ -0,0 +1,16 @@ +// RUN: %clang %s -o %t && %run %t 2>&1 + +// There's no interceptor for strcasestr on Windows +// XFAIL: win32 + +#define _GNU_SOURCE +#include +#include +int main(int argc, char **argv) { + char *r = 0; + char s1[] = "aB"; + char s2[] = "b"; + r = strcasestr(s1, s2); + assert(r == s1 + 1); + return 0; +} Index: test/sanitizer_common/TestCases/strcspn.c =================================================================== --- /dev/null +++ test/sanitizer_common/TestCases/strcspn.c @@ -0,0 +1,13 @@ +// RUN: %clang %s -o %t && %run %t 2>&1 + +#include +#include + +int main(int argc, char **argv) { + size_t r; + char s1[] = "ad"; + char s2[] = "cd"; + r = strcspn(s1, s2); + assert(r == 1); + return 0; +} Index: test/sanitizer_common/TestCases/strpbrk.c =================================================================== --- /dev/null +++ test/sanitizer_common/TestCases/strpbrk.c @@ -0,0 +1,14 @@ +// RUN: %clang %s -o %t && %run %t 2>&1 + +#include +#include + + +int main(int argc, char **argv) { + char *r = 0; + char s1[] = "ad"; + char s2[] = "cd"; + r = strpbrk(s1, s2); + assert(r == s1 + 1); + return 0; +} Index: test/sanitizer_common/TestCases/strstr.c =================================================================== --- /dev/null +++ test/sanitizer_common/TestCases/strstr.c @@ -0,0 +1,12 @@ +// RUN: %clang %s -o %t && %run %t 2>&1 + +#include +#include +int main(int argc, char **argv) { + char *r = 0; + char s1[] = "ab"; + char s2[] = "b"; + r = strstr(s1, s2); + assert(r == s1 + 1); + return 0; +}