diff --git a/compiler-rt/lib/asan/asan_interceptors.cpp b/compiler-rt/lib/asan/asan_interceptors.cpp --- a/compiler-rt/lib/asan/asan_interceptors.cpp +++ b/compiler-rt/lib/asan/asan_interceptors.cpp @@ -463,6 +463,7 @@ return REAL(strcpy)(to, from); } +#if !SANITIZER_WINDOWS INTERCEPTOR(char*, strdup, const char *s) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strdup); @@ -479,6 +480,24 @@ } return reinterpret_cast(new_mem); } +#endif // !SANITIZER_WINDOWS + +#if SANITIZER_WINDOWS +INTERCEPTOR(char*, _strdup, const char *s) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, _strdup); + if (UNLIKELY(!asan_inited)) return internal_strdup(s); + ENSURE_ASAN_INITED(); + uptr length = REAL(strlen)(s); + if (flags()->replace_str) { + ASAN_READ_RANGE(ctx, s, length + 1); + } + GET_STACK_TRACE_MALLOC; + void *new_mem = asan_malloc(length + 1, &stack); + REAL(memcpy)(new_mem, s, length + 1); + return reinterpret_cast(new_mem); +} +#endif // SANITIZER_WINDOWS #if ASAN_INTERCEPT___STRDUP INTERCEPTOR(char*, __strdup, const char *s) { @@ -664,7 +683,11 @@ ASAN_INTERCEPT_FUNC(strcpy); ASAN_INTERCEPT_FUNC(strncat); ASAN_INTERCEPT_FUNC(strncpy); +#if SANITIZER_WINDOWS + ASAN_INTERCEPT_FUNC(_strdup); +#else ASAN_INTERCEPT_FUNC(strdup); +#endif #if ASAN_INTERCEPT___STRDUP ASAN_INTERCEPT_FUNC(__strdup); #endif diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -199,12 +199,15 @@ #endif // Platform-specific options. -#if SANITIZER_APPLE -#define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE 0 -#elif SANITIZER_WINDOWS64 +#if SANITIZER_APPLE || (SANITIZER_WINDOWS && SANITIZER_X64) #define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE 0 +#define PLATFORM_MEMCPY_MEMMOVE_CAN_BE_FOLDED 0 +#elif SANITIZER_WINDOWS && SANITIZER_I386 +#define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE 1 +#define PLATFORM_MEMCPY_MEMMOVE_CAN_BE_FOLDED 1 #else #define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE 1 +#define PLATFORM_MEMCPY_MEMMOVE_CAN_BE_FOLDED 0 #endif // SANITIZER_APPLE #ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE @@ -871,16 +874,29 @@ // N.B.: If we switch this to internal_ we'll have to use internal_memmove // due to memcpy being an alias of memmove on OS X. void *ctx; -#if PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE +#ifndef PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE +#error "Undefined" +#endif + +#ifndef PLATFORM_MEMCPY_MEMMOVE_CAN_BE_FOLDED +#error "Undefined" +#endif +#if PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE && \ + !PLATFORM_MEMCPY_MEMMOVE_CAN_BE_FOLDED COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, dst, src, size); #else COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, dst, src, size); #endif } +// On i386 Windows memcpy and memmove are implemented identically but they +// have two different definitions, so they can have distinct locations in +// memory. This means that memcpy may still needs to be intercepted. #define INIT_MEMCPY \ do { \ - if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { \ + if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE || \ + (PLATFORM_MEMCPY_MEMMOVE_CAN_BE_FOLDED && \ + REAL(memcpy) != REAL(memmove))) { \ COMMON_INTERCEPT_FUNCTION(memcpy); \ } else { \ ASSIGN_REAL(memcpy, memmove); \ diff --git a/compiler-rt/test/asan/TestCases/Windows/memcpy_sanity.cpp b/compiler-rt/test/asan/TestCases/Windows/memcpy_sanity.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/memcpy_sanity.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cl_asan %s -Fe%t.icf.ref /clang:-fno-builtin-memcpy /clang:-fno-builtin-memmove /link /OPT:ICF /OPT:REF +// RUN: %clang_cl_asan %s -Fe%t.noicf.ref /clang:-fno-builtin-memcpy /clang:-fno-builtin-memmove /link /OPT:NOICF /OPT:REF +// RUN: %clang_cl_asan %s -Fe%t.icf.noref /clang:-fno-builtin-memcpy /clang:-fno-builtin-memmove /link /OPT:ICF /OPT:NOREF +// RUN: %clang_cl_asan %s -Fe%t.noicf.noref /clang:-fno-builtin-memcpy /clang:-fno-builtin-memmove /link /OPT:NOICF /OPT:NOREF +// RUN: not %run %t.icf.ref 2>&1 | FileCheck %s +// RUN: not %run %t.noicf.ref 2>&1 | FileCheck %s +// RUN: not %run %t.icf.noref 2>&1 | FileCheck %s +// RUN: not %run %t.noicf.noref 2>&1 | FileCheck %s + +#include + +int main() { + int *buf = new int[4]; + + memmove(buf + 1, buf, 3 * sizeof(int)); + memcpy(buf + 1, buf, 3 * sizeof(int)); + + delete[] buf; + + memcpy(buf + 1, buf, 3 * sizeof(int)); + // CHECK: AddressSanitizer: heap-use-after-free + + return 0; +} diff --git a/compiler-rt/test/asan/TestCases/Windows/memmove_sanity.cpp b/compiler-rt/test/asan/TestCases/Windows/memmove_sanity.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/memmove_sanity.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cl_asan %s -Fe%t.icf.ref /clang:-fno-builtin-memcpy /clang:-fno-builtin-memmove /link /OPT:ICF /OPT:REF +// RUN: %clang_cl_asan %s -Fe%t.noicf.ref /clang:-fno-builtin-memcpy /clang:-fno-builtin-memmove /link /OPT:NOICF /OPT:REF +// RUN: %clang_cl_asan %s -Fe%t.icf.noref /clang:-fno-builtin-memcpy /clang:-fno-builtin-memmove /link /OPT:ICF /OPT:NOREF +// RUN: %clang_cl_asan %s -Fe%t.noicf.noref /clang:-fno-builtin-memcpy /clang:-fno-builtin-memmove /link /OPT:NOICF /OPT:NOREF +// RUN: not %run %t.icf.ref 2>&1 | FileCheck %s +// RUN: not %run %t.noicf.ref 2>&1 | FileCheck %s +// RUN: not %run %t.icf.noref 2>&1 | FileCheck %s +// RUN: not %run %t.noicf.noref 2>&1 | FileCheck %s + +#include + +int main() { + int *buf = new int[4]; + + memmove(buf + 1, buf, 3 * sizeof(int)); + memcpy(buf + 1, buf, 3 * sizeof(int)); + + delete[] buf; + + memmove(buf + 1, buf, 3 * sizeof(int)); + // CHECK: AddressSanitizer: heap-use-after-free + + return 0; +}