diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp --- a/compiler-rt/lib/asan/asan_allocator.cpp +++ b/compiler-rt/lib/asan/asan_allocator.cpp @@ -714,15 +714,31 @@ void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); if (new_ptr) { - u8 chunk_state = atomic_load(&m->chunk_state, memory_order_acquire); - if (chunk_state != CHUNK_ALLOCATED) - ReportInvalidFree(old_ptr, chunk_state, stack); - CHECK_NE(REAL(memcpy), nullptr); - uptr memcpy_size = Min(new_size, m->UsedSize()); - // If realloc() races with free(), we may start copying freed memory. - // However, we will report racy double-free later anyway. - REAL(memcpy)(new_ptr, old_ptr, memcpy_size); - Deallocate(old_ptr, 0, 0, stack, FROM_MALLOC); +#if SANITIZER_WINDOWS + // On Windows, uninstrumented DLLs may allocate memory before ASan hooks + // malloc. Don't report an invalid free in this case. + if (!get_allocator().PointerIsMine(old_ptr)) { + if (!IsSystemHeapAddress(p)) + ReportFreeNotMalloced(p, stack); + CHECK_NE(REAL(memcpy), nullptr); + CHECK_NE(REAL(_msize), nullptr); + CHECK_NE(REAL(free), nullptr); + uptr memcpy_size = Min(new_size, REAL(_msize)(old_ptr)); + REAL(memcpy)(new_ptr, old_ptr, memcpy_size); + REAL(free)(old_ptr); + } else +#endif + { + u8 chunk_state = atomic_load(&m->chunk_state, memory_order_acquire); + if (chunk_state != CHUNK_ALLOCATED) + ReportInvalidFree(old_ptr, chunk_state, stack); + CHECK_NE(REAL(memcpy), nullptr); + uptr memcpy_size = Min(new_size, m->UsedSize()); + // If realloc() races with free(), we may start copying freed memory. + // However, we will report racy double-free later anyway. + REAL(memcpy)(new_ptr, old_ptr, memcpy_size); + Deallocate(old_ptr, 0, 0, stack, FROM_MALLOC); + } } return new_ptr; } diff --git a/compiler-rt/lib/asan/asan_interceptors.h b/compiler-rt/lib/asan/asan_interceptors.h --- a/compiler-rt/lib/asan/asan_interceptors.h +++ b/compiler-rt/lib/asan/asan_interceptors.h @@ -133,6 +133,11 @@ DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen) DECLARE_REAL(char*, strstr, const char *s1, const char *s2) +# if SANITIZER_WINDOWS +DECLARE_REAL(SIZE_T, _msize, void *ptr) +DECLARE_REAL(void, free, void *ptr) +# endif + # if !SANITIZER_APPLE # define ASAN_INTERCEPT_FUNC(name) \ do { \ diff --git a/compiler-rt/lib/asan/asan_malloc_win.cpp b/compiler-rt/lib/asan/asan_malloc_win.cpp --- a/compiler-rt/lib/asan/asan_malloc_win.cpp +++ b/compiler-rt/lib/asan/asan_malloc_win.cpp @@ -64,6 +64,9 @@ # define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE #endif +DEFINE_REAL(SIZE_T, _msize, void *ptr) +DEFINE_REAL(void, free, void *ptr) + extern "C" { ALLOCATION_FUNCTION_ATTRIBUTE size_t _msize(void *ptr) { @@ -214,6 +217,8 @@ if (!asan_inited || OWNED_BY_RTL(hHeap, lpMem)) return REAL(HeapSize)(hHeap, dwFlags, lpMem); } else { + if (!__sanitizer_get_ownership(lpMem)) + return REAL(HeapSize)(hHeap, dwFlags, lpMem); CHECK(dwFlags == 0 && "unsupported heap flags"); } GET_CURRENT_PC_BP_SP; @@ -479,16 +484,16 @@ namespace __asan { -static void TryToOverrideFunction(const char *fname, uptr new_func) { +static void TryToOverrideFunction(const char *fname, uptr new_func, uptr *orig_old_func = 0) { // Failure here is not fatal. The CRT may not be present, and different CRT // versions use different symbols. - if (!__interception::OverrideFunction(fname, new_func)) + if (!__interception::OverrideFunction(fname, new_func, orig_old_func)) VPrintf(2, "Failed to override function %s\n", fname); } void ReplaceSystemMalloc() { #if defined(ASAN_DYNAMIC) - TryToOverrideFunction("free", (uptr)free); + TryToOverrideFunction("free", (uptr)free, (uptr *)&REAL(free)); TryToOverrideFunction("_free_base", (uptr)free); TryToOverrideFunction("malloc", (uptr)malloc); TryToOverrideFunction("_malloc_base", (uptr)malloc); @@ -502,7 +507,7 @@ TryToOverrideFunction("_recalloc", (uptr)_recalloc); TryToOverrideFunction("_recalloc_base", (uptr)_recalloc); TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc); - TryToOverrideFunction("_msize", (uptr)_msize); + TryToOverrideFunction("_msize", (uptr)_msize, (uptr *)&REAL(_msize)); TryToOverrideFunction("_msize_base", (uptr)_msize); TryToOverrideFunction("_expand", (uptr)_expand); TryToOverrideFunction("_expand_base", (uptr)_expand); diff --git a/compiler-rt/test/asan/TestCases/Windows/dll_heap_allocation_realloc.cpp b/compiler-rt/test/asan/TestCases/Windows/dll_heap_allocation_realloc.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/dll_heap_allocation_realloc.cpp @@ -0,0 +1,49 @@ +// RUN: %clangxx -g -shared %s -o %t-1.dll -DHEAP_LIBRARY=1 -Wl,--exclude-all-symbols -Wl,--out-implib,%t-1.dll.a \ +// RUN: || %clang_cl -LD %s -Fe%t-1.dll -DHEAP_LIBRARY=1 -MD +// RUN: %clangxx_asan -shared %s -o %t-2.dll -DHEAP_LIBRARY=2 -Wl,--exclude-all-symbols -Wl,--out-implib,%t-2.dll.a \ +// RUN: || %clang_cl -LD %s -Fe%t-2.dll -DHEAP_LIBRARY=2 -fsanitize=address -MD +// RUN: %clangxx -g %s %t-1.dll.a %t-2.dll.a -o %t \ +// RUN: || %clang_cl %s %t-1.lib %t-2.lib -Fe%t -MT +// RUN: %run %t 2>&1 | FileCheck %s + +// Check that ASan does not fail when realloc-ing allocations that occurred +// within an uninstrumented DLL. + +#if HEAP_LIBRARY == 1 + +# include +# include + +__declspec(dllexport) int *myglobal = [] { + auto *p = reinterpret_cast(malloc(sizeof(int))); + *p = 42; + return p; +}(); +BOOL WINAPI DllMain(PVOID h, DWORD reason, PVOID reserved) { return TRUE; } + +#elif HEAP_LIBRARY == 2 + +# include + +// This export is only to force linking of the DLL. +__declspec(dllexport) int g_heap_library_2 = 0; +BOOL WINAPI DllMain(PVOID h, DWORD reason, PVOID reserved) { return TRUE; } + +#else + +# include +# include + +__declspec(dllimport) extern int *myglobal; +__declspec(dllimport) int g_heap_library_2; +int main(int argc, char **argv) { + g_heap_library_2++; + myglobal = reinterpret_cast(realloc(myglobal, sizeof(int) * 2)); + printf("myglobal: %d\n", *myglobal); + return 0; +} + +#endif + +// CHECK-NOT: ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed +// CHECK: myglobal: 42