Index: compiler-rt/lib/asan/asan_malloc_win.cc =================================================================== --- compiler-rt/lib/asan/asan_malloc_win.cc +++ compiler-rt/lib/asan/asan_malloc_win.cc @@ -21,10 +21,9 @@ typedef const void *LPCVOID; typedef void *LPVOID; -#define HEAP_ZERO_MEMORY 0x00000008 +#define HEAP_ZERO_MEMORY 0x00000008 #define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010 - #include "asan_allocator.h" #include "asan_interceptors.h" #include "asan_internal.h" @@ -41,11 +40,21 @@ // so we have to intercept them before they are called for the first time. #if ASAN_DYNAMIC -# define ALLOCATION_FUNCTION_ATTRIBUTE +#define ALLOCATION_FUNCTION_ATTRIBUTE #else -# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +#define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE #endif +ALLOCATION_FUNCTION_ATTRIBUTE +size_t _msize(void *ptr) { + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(ptr, pc, bp); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +size_t _msize_base(void *ptr) { return _msize(ptr); } + extern "C" { ALLOCATION_FUNCTION_ATTRIBUTE void free(void *ptr) { @@ -54,14 +63,10 @@ } ALLOCATION_FUNCTION_ATTRIBUTE -void _free_dbg(void *ptr, int) { - free(ptr); -} +void _free_dbg(void *ptr, int) { free(ptr); } ALLOCATION_FUNCTION_ATTRIBUTE -void _free_base(void *ptr) { - free(ptr); -} +void _free_base(void *ptr) { free(ptr); } ALLOCATION_FUNCTION_ATTRIBUTE void *malloc(size_t size) { @@ -70,14 +75,10 @@ } ALLOCATION_FUNCTION_ATTRIBUTE -void *_malloc_base(size_t size) { - return malloc(size); -} +void *_malloc_base(size_t size) { return malloc(size); } ALLOCATION_FUNCTION_ATTRIBUTE -void *_malloc_dbg(size_t size, int, const char *, int) { - return malloc(size); -} +void *_malloc_dbg(size_t size, int, const char *, int) { return malloc(size); } ALLOCATION_FUNCTION_ATTRIBUTE void *calloc(size_t nmemb, size_t size) { @@ -86,9 +87,7 @@ } ALLOCATION_FUNCTION_ATTRIBUTE -void *_calloc_base(size_t nmemb, size_t size) { - return calloc(nmemb, size); -} +void *_calloc_base(size_t nmemb, size_t size) { return calloc(nmemb, size); } ALLOCATION_FUNCTION_ATTRIBUTE void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) { @@ -113,9 +112,7 @@ } ALLOCATION_FUNCTION_ATTRIBUTE -void *_realloc_base(void *ptr, size_t size) { - return realloc(ptr, size); -} +void *_realloc_base(void *ptr, size_t size) { return realloc(ptr, size); } ALLOCATION_FUNCTION_ATTRIBUTE void *_recalloc(void *p, size_t n, size_t elem_size) { @@ -124,7 +121,15 @@ const size_t size = n * elem_size; if (elem_size != 0 && size / elem_size != n) return 0; - return realloc(p, size); + size_t old_size = _msize(p); + void *new_alloc = malloc(size); + if (new_alloc) { + REAL(memcpy)(new_alloc, p, Min(size, old_size)); + if (old_size < size) + REAL(memset)(((BYTE *)new_alloc) + old_size, 0, size - old_size); + free(p); + } + return new_alloc; } ALLOCATION_FUNCTION_ATTRIBUTE @@ -132,18 +137,6 @@ return _recalloc(p, n, elem_size); } -ALLOCATION_FUNCTION_ATTRIBUTE -size_t _msize(void *ptr) { - GET_CURRENT_PC_BP_SP; - (void)sp; - return asan_malloc_usable_size(ptr, pc, bp); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -size_t _msize_base(void *ptr) { - return _msize(ptr); -} - ALLOCATION_FUNCTION_ATTRIBUTE void *_expand(void *memblock, size_t size) { // _expand is used in realloc-like functions to resize the buffer if possible. @@ -159,19 +152,16 @@ // TODO(timurrrr): Might want to add support for _aligned_* allocation // functions to detect a bit more bugs. Those functions seem to wrap malloc(). -int _CrtDbgReport(int, const char*, int, - const char*, const char*, ...) { +int _CrtDbgReport(int, const char *, int, const char *, const char *, ...) { ShowStatsAndAbort(); } -int _CrtDbgReportW(int reportType, const wchar_t*, int, - const wchar_t*, const wchar_t*, ...) { +int _CrtDbgReportW(int reportType, const wchar_t *, int, const wchar_t *, + const wchar_t *, ...) { ShowStatsAndAbort(); } -int _CrtSetReportMode(int, int) { - return 0; -} +int _CrtSetReportMode(int, int) { return 0; } } // extern "C" INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags, @@ -198,11 +188,33 @@ INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes) { GET_STACK_TRACE_MALLOC; + GET_CURRENT_PC_BP_SP; // Realloc should never reallocate in place. if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) return nullptr; CHECK(dwFlags == 0 && "unsupported heap flags"); - return asan_realloc(lpMem, dwBytes, &stack); + + // HeapReAlloc and HeapAlloc both happily accept 0 sized allocations. + // passing a 0 size into asan_realloc will free the allocation. + // To avoid this and keep behavior consistent, fudge the size if 0. + // (asan_malloc already does this) + if (dwBytes == 0) + dwBytes = 1; + + size_t old_size; + if (dwFlags & HEAP_ZERO_MEMORY) + old_size = asan_malloc_usable_size(lpMem, pc, bp); + + void *ptr = asan_realloc(lpMem, dwBytes, &stack); + if (ptr == nullptr) + return nullptr; + + size_t new_size = asan_malloc_usable_size(ptr, pc, bp); + if ((dwFlags & HEAP_ZERO_MEMORY) && (old_size < new_size)) { + REAL(memset)(((BYTE *)ptr) + old_size, 0, new_size - old_size); + } + + return ptr; } INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags, Index: compiler-rt/test/asan/TestCases/Windows/heapalloc_zero_size.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/heapalloc_zero_size.cc @@ -0,0 +1,22 @@ +// RUN: %clang_cl_asan /Od -o %t %s +// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %clang_cl /Od -o %t %s +// RUN: %run %t 2>&1 | FileCheck %s + +#include +#include +#include + +int main() { + HANDLE heap = HeapCreate(0, 0, 0); + void *ptr = HeapAlloc(heap, 0, 4); + assert(ptr); + void *ptr2 = HeapReAlloc(heap, 0, ptr, 0); + assert(ptr2); + HeapFree(heap, 0, ptr2); + fprintf(stderr, "passed!\n"); +} + +// CHECK-NOT: double-free +// CHECK-NOT: AddressSanitizer +// CHECK: passed! Index: compiler-rt/test/asan/TestCases/Windows/recalloc_sanity.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/recalloc_sanity.cc @@ -0,0 +1,37 @@ +// RUN: %clang_cl_asan %s -o %t.exe +// RUN: %run %t.exe 2>&1 | FileCheck %s +// RUN: %clang_cl %s -o %t.exe +// RUN: %run %t.exe 2>&1 | FileCheck %s + +#include +#include +#include + +int main() { + void *p = calloc(1, 100); + assert(p); + void *np = _recalloc(p, 2, 100); + assert(np); + for (int i = 0; i < 2 * 100; i++) { + assert(((BYTE *)np)[i] == 0); + } + void *nnp = _recalloc(np, 1, 100); + assert(nnp); + for (int i = 0; i < 100; i++) { + assert(((BYTE *)nnp)[i] == 0); + ((BYTE *)nnp)[i] = 0x0d; + } + void *nnnp = _recalloc(nnp, 2, 100); + assert(nnnp); + for (int i = 0; i < 100; i++) { + assert(((BYTE *)nnnp)[i] == 0x0d); + } + for (int i = 100; i < 200; i++) { + assert(((BYTE *)nnnp)[i] == 0); + } + fprintf(stderr, "passed\n"); + return 0; +} + +// CHECK-NOT: Assertion +// CHECK: passed \ No newline at end of file