diff --git a/compiler-rt/lib/asan/asan_malloc_win.cc b/compiler-rt/lib/asan/asan_malloc_win.cc --- a/compiler-rt/lib/asan/asan_malloc_win.cc +++ b/compiler-rt/lib/asan/asan_malloc_win.cc @@ -19,7 +19,6 @@ #include "asan_internal.h" #include "asan_stack.h" #include "interception/interception.h" -#include // Intentionally not including windows.h here, to avoid the risk of // pulling in conflicting declarations of these functions. (With mingw-w64, @@ -28,6 +27,10 @@ typedef void *HANDLE; typedef const void *LPCVOID; typedef void *LPVOID; +typedef HANDLE HWND; +typedef HANDLE HGLOBAL; +typedef HANDLE HLOCAL; +typedef unsigned int UINT; typedef unsigned long DWORD; constexpr unsigned long HEAP_ZERO_MEMORY = 0x00000008; @@ -43,7 +46,6 @@ constexpr unsigned long HEAP_REALLOC_UNSUPPORTED_FLAGS = (~HEAP_ALLOCATE_SUPPORTED_FLAGS); - extern "C" { LPVOID WINAPI HeapAlloc(HANDLE hHeap, DWORD dwFlags, size_t dwBytes); LPVOID WINAPI HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, @@ -52,6 +54,22 @@ size_t WINAPI HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); BOOL WINAPI HeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); +HANDLE WINAPI GetProcessHeap(); + +_declspec(dllimport) HGLOBAL WINAPI GlobalAlloc(UINT uFlags, SIZE_T dwBytes); +_declspec(dllimport) HGLOBAL WINAPI GlobalFree(HGLOBAL hMem); +_declspec(dllimport) HGLOBAL WINAPI GlobalSize(HGLOBAL hMem); +_declspec(dllimport) HGLOBAL WINAPI + GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes, UINT uFlags); +_declspec(dllimport) HGLOBAL WINAPI GlobalLock(HGLOBAL hMem); +_declspec(dllimport) HGLOBAL WINAPI GlobalUnlock(HGLOBAL hMem); +_declspec(dllimport) HLOCAL WINAPI LocalAlloc(UINT uFlags, SIZE_T dwBytes); +_declspec(dllimport) HLOCAL WINAPI LocalFree(HLOCAL hMem); +_declspec(dllimport) HLOCAL WINAPI LocalSize(HLOCAL hMem); +_declspec(dllimport) HLOCAL WINAPI + LocalReAlloc(HLOCAL hMem, size_t dwBytes, UINT uFlags); +_declspec(dllimport) HLOCAL WINAPI LocalLock(HLOCAL hMem); +_declspec(dllimport) HLOCAL WINAPI LocalUnlock(HLOCAL hMem); } using namespace __asan; // NOLINT @@ -480,6 +498,286 @@ Flags, BaseAddress, Size); } +// FIXED and ZEROINIT correspond to LMEM_FIXED/GMEM_FIXED +// and LMEM_ZEROINIT/GMEM_ZEROINIT (as provided in the documentation). +// In case, if these values change then FIXED and ZEROINIT +// will have to be updated accordingly. +#define FIXED 0x0000 +#define ZEROINIT 0x0040 + +constexpr unsigned long SHARED_ALLOC_SUPPORTED_FLAGS = (FIXED | ZEROINIT); +constexpr unsigned long SHARED_ALLOC_UNSUPPORTED_FLAGS = + (~SHARED_ALLOC_SUPPORTED_FLAGS); + +INTERCEPTOR_WINAPI(HGLOBAL, GlobalAlloc, UINT uFlags, SIZE_T dwBytes) { + // If we encounter an unsupported flag, then we fall + // back to the original allocator. + if (uFlags & SHARED_ALLOC_UNSUPPORTED_FLAGS) { + return REAL(GlobalAlloc)(uFlags, dwBytes); + } + + GET_STACK_TRACE_MALLOC; + if (uFlags & ZEROINIT) + return asan_calloc(dwBytes, 1, &stack); + else + return asan_malloc(dwBytes, &stack); +} + +INTERCEPTOR_WINAPI(HGLOBAL, GlobalFree, HGLOBAL hMem) { + // If the memory we are trying to free is not owned + // by ASan heap, then fall back to the original GlobalFree. + if (OWNED_BY_RTL(GetProcessHeap(), hMem)) { + return REAL(GlobalFree)(hMem); + } + GET_STACK_TRACE_FREE; + asan_free(hMem, &stack, FROM_MALLOC); + return nullptr; +} + +INTERCEPTOR_WINAPI(SIZE_T, GlobalSize, HGLOBAL hMem) { + // We need to check whether the ASAN allocator owns the pointer + // we're about to use. Allocations might occur before interception + // takes place, so if it is not owned by RTL heap, the we can + // pass it to ASAN heap for inspection. + if (!asan_inited || OWNED_BY_RTL(GetProcessHeap(), hMem)) + return REAL(GlobalSize)(hMem); + + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(hMem, pc, bp); +} + +namespace __asan { +using GlobalLocalAlloc = HANDLE(WINAPI *)(UINT, SIZE_T); +using GlobalLocalRealloc = HANDLE(WINAPI *)(HANDLE, SIZE_T, UINT); +using GlobalLocalSize = SIZE_T(WINAPI *)(HANDLE); +using GlobalLocalFree = HANDLE(WINAPI *)(HANDLE); +using GlobalLocalLock = LPVOID(WINAPI *)(HANDLE); +using GlobalLocalUnlock = LPVOID(WINAPI *)(HANDLE); + +enum class AllocationOwnership { + OWNED_BY_UNKNOWN, + OWNED_BY_ASAN, + OWNED_BY_RTL, + OWNED_BY_GLOBAL_OR_LOCAL, + OWNED_BY_GLOBAL_OR_LOCAL_HANDLE, +}; + +void *RtlToAsan(void *mPtr, size_t old_size, size_t dwBytes, + GlobalLocalFree freeFunc, BufferedStackTrace *stack) { + // Transfer from RTL owned allocation to ASAN owned allocation + void *replacement_alloc; + replacement_alloc = asan_calloc(dwBytes, 1, stack); + if (replacement_alloc) { + if (old_size == ((SIZE_T)0) - 1) { + asan_free(replacement_alloc, stack, FROM_MALLOC); + return nullptr; + } + REAL(memcpy)(replacement_alloc, mPtr, old_size); + freeFunc((HANDLE)mPtr); + } + return replacement_alloc; +} + +void *AsanToRtl(void *mPtr, UINT uFlags, SIZE_T dwBytes, + GlobalLocalAlloc allocFunc, GlobalLocalLock lockFunc, + GlobalLocalLock unlockFunc, BufferedStackTrace *stack, uptr pc, + uptr bp) { + // Transfer from ASAN owned allocation to RTL owned allocation + void *replacement_alloc; + HANDLE mem = allocFunc(uFlags, dwBytes); + // GlobalLock/LocalLock return a pointer to the memory owned by the mem + // handle. We need the pointer to copy the data over from the ASAN owned + // memory. + replacement_alloc = (void *)lockFunc(mem); + size_t old_usable_size = 0; + if (replacement_alloc) { + old_usable_size = asan_malloc_usable_size(mPtr, pc, bp); + REAL(memcpy)(replacement_alloc, mPtr, Min(dwBytes, old_usable_size)); + asan_free(mPtr, stack, FROM_MALLOC); + } + unlockFunc(mem); + return replacement_alloc; +} + +void *ReAllocToAsan(UINT uFlags, void *mPtr, size_t dwBytes, uptr pc, uptr bp, + BufferedStackTrace *stack) { + // GlobalAlloc, LocalAlloc, GlocalReAlloc and LocalReAlloc all + // accept 0 sized allocations. Passing a zero size into asan_realloc will + // free the allocation. To avoid this and keep behavior consistent, fudge + // the size if zero (asan_malloc already does this). + if (dwBytes == 0) + dwBytes = 1; + + size_t old_size; + if (uFlags & ZEROINIT) + old_size = asan_malloc_usable_size(mPtr, pc, bp); + + void *ptr = asan_realloc(mPtr, dwBytes, stack); + if (ptr == nullptr) + return nullptr; + + if (uFlags & ZEROINIT) { + size_t new_size = asan_malloc_usable_size(ptr, pc, bp); + if (old_size < new_size) + REAL(memset)(((u8 *)ptr) + old_size, 0, new_size - old_size); + } + return ptr; +} + +AllocationOwnership CheckGlobalLocalHeapOwnership( + HANDLE hMem, GlobalLocalLock lockFunc, GlobalLocalUnlock unlockFunc) { + /* To figure the validity of hMem, we use GlobalLock/LocalLock. Those two + * functions can return three things: (1) the pointer that's passed in, in + * which case it is a pointer owned by the Global/Local heap (2) the pointer + * to the allocated object if it's a Global/Local heap HANDLE (3) nullptr if it's + * a pointer which does not belong to the Global/Local heap Using these three + * return types, we figure out if the pointer is TYPE_VALID_PTR or TYPE_HANDLE + * or TYPE_UNKNOWN_PTR + * + * NOTE: As an implementation detail, movable memory objects also live on the + * heap. HeapValidate will return true if given a moveable memory handle. + * + */ + + // Do this first to avoid expensive checks if the pointer is owned by ASAN. + if (__sanitizer_get_ownership(hMem)) { + return AllocationOwnership::OWNED_BY_ASAN; + } + + // It is not safe to pass wild pointers to GlobalLock/LocalLock. + if (HeapValidate(GetProcessHeap(), 0, hMem)) { + void *ptr = lockFunc(hMem); + // We don't care whether ptr is moved after this point as we're just trying + // to determine where it came from. + unlockFunc(hMem); + if (ptr == hMem) { + return AllocationOwnership::OWNED_BY_GLOBAL_OR_LOCAL; + } else if (ptr != nullptr) { + return AllocationOwnership::OWNED_BY_GLOBAL_OR_LOCAL_HANDLE; + } + } + return AllocationOwnership::OWNED_BY_UNKNOWN; +} + +void *ReAllocGlobalLocal(GlobalLocalRealloc reallocFunc, + GlobalLocalSize sizeFunc, GlobalLocalFree freeFunc, + GlobalLocalAlloc allocFunc, GlobalLocalLock lockFunc, + GlobalLocalUnlock unlockFunc, HANDLE hMem, + DWORD dwBytes, UINT uFlags) { + CHECK(reallocFunc && sizeFunc && freeFunc && allocFunc); + GET_STACK_TRACE_MALLOC; + GET_CURRENT_PC_BP_SP; + (void)sp; + + bool only_asan_supported_flags = + (SHARED_ALLOC_UNSUPPORTED_FLAGS & uFlags) == 0; + + AllocationOwnership ownershipState = + CheckGlobalLocalHeapOwnership(hMem, lockFunc, unlockFunc); + + // If this global block which was allocated before the ASAN + // runtime came up OR if mPtr is invalid, use the real GlobalReAlloc function. + if (UNLIKELY(!asan_inited) || + ownershipState == AllocationOwnership::OWNED_BY_GLOBAL_OR_LOCAL_HANDLE) { + return reallocFunc(hMem, dwBytes, uFlags); + } + + // Since hMem is not a handle to moveable memory we may safely cast it to + // pointer. + void *mPtr = (void *)hMem; + + if (ownershipState == AllocationOwnership::OWNED_BY_GLOBAL_OR_LOCAL || + (ownershipState == AllocationOwnership::OWNED_BY_UNKNOWN && + !only_asan_supported_flags)) { + if (only_asan_supported_flags) { + // if this is a conversion to ASAN supported flags, transfer this + // allocation to the ASAN allocator + return RtlToAsan(mPtr, sizeFunc(mPtr), dwBytes, freeFunc, &stack); + } else { + // owned by this heap or neither with unsupported ASAN flags, + // just pass back to original allocator + CHECK(ownershipState == AllocationOwnership::OWNED_BY_GLOBAL_OR_LOCAL || + ownershipState == AllocationOwnership::OWNED_BY_UNKNOWN); + CHECK(!only_asan_supported_flags); + return reallocFunc(mPtr, dwBytes, uFlags); + } + } + + if (ownershipState == AllocationOwnership::OWNED_BY_ASAN && + !only_asan_supported_flags) { + // Conversion to unsupported flags allocation, + // transfer this allocation back to the original allocator. + return AsanToRtl(mPtr, uFlags, dwBytes, allocFunc, lockFunc, unlockFunc, + &stack, pc, bp); + } + + CHECK((ownershipState == AllocationOwnership::OWNED_BY_ASAN || + ownershipState == AllocationOwnership::OWNED_BY_RTL || + ownershipState == AllocationOwnership::OWNED_BY_UNKNOWN) && + only_asan_supported_flags); + + return ReAllocToAsan(uFlags, mPtr, dwBytes, pc, bp, &stack); +} +} // namespace __asan + +INTERCEPTOR_WINAPI(HGLOBAL, GlobalReAlloc, HGLOBAL hMem, DWORD dwBytes, + UINT uFlags) { + return ReAllocGlobalLocal( + (GlobalLocalRealloc)REAL(GlobalReAlloc), + (GlobalLocalSize)REAL(GlobalSize), (GlobalLocalFree)REAL(GlobalFree), + (GlobalLocalAlloc)REAL(GlobalAlloc), (GlobalLocalLock)GlobalLock, + (GlobalLocalUnlock)GlobalUnlock, (HANDLE)hMem, dwBytes, uFlags); +} + +INTERCEPTOR_WINAPI(HLOCAL, LocalAlloc, UINT uFlags, SIZE_T uBytes) { + // If we encounter an unsupported flag, then we fall + // back to the original allocator. + if (uFlags & SHARED_ALLOC_UNSUPPORTED_FLAGS) { + return REAL(LocalAlloc)(uFlags, uBytes); + } + + GET_STACK_TRACE_MALLOC; + if (uFlags & ZEROINIT) + return asan_calloc(uBytes, 1, &stack); + else + return asan_malloc(uBytes, &stack); +} + +INTERCEPTOR_WINAPI(HLOCAL, LocalFree, HGLOBAL hMem) { + // If the memory we are trying to free is not owned + // ASan heap, then fall back to the original LocalFree. + if (OWNED_BY_RTL(GetProcessHeap(), hMem)) { + return REAL(LocalFree)(hMem); + } + + GET_STACK_TRACE_FREE; + asan_free(hMem, &stack, FROM_MALLOC); + return nullptr; +} + +INTERCEPTOR_WINAPI(SIZE_T, LocalSize, HGLOBAL hMem) { + // We need to check whether the ASAN allocator owns the pointer + // we're about to use. Allocations might occur before interception + // takes place, so if it is not owned by RTL heap, the we can + // pass it to ASAN heap for inspection. + if (!asan_inited || OWNED_BY_RTL(GetProcessHeap(), hMem)) + return REAL(LocalSize)(hMem); + + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(hMem, pc, bp); +} + +INTERCEPTOR_WINAPI(HLOCAL, LocalReAlloc, HGLOBAL hMem, DWORD dwBytes, + UINT uFlags) { + return ReAllocGlobalLocal( + (GlobalLocalRealloc)REAL(LocalReAlloc), (GlobalLocalSize)REAL(LocalSize), + (GlobalLocalFree)REAL(LocalFree), (GlobalLocalAlloc)REAL(LocalAlloc), + (GlobalLocalLock)LocalLock, (GlobalLocalUnlock)LocalUnlock, (HANDLE)hMem, + dwBytes, uFlags); +} + namespace __asan { static void TryToOverrideFunction(const char *fname, uptr new_func) { @@ -511,6 +809,14 @@ TryToOverrideFunction("_expand_base", (uptr)_expand); if (flags()->windows_hook_rtl_allocators) { + INTERCEPT_FUNCTION(GlobalAlloc); + INTERCEPT_FUNCTION(GlobalFree); + INTERCEPT_FUNCTION(GlobalSize); + INTERCEPT_FUNCTION(GlobalReAlloc); + INTERCEPT_FUNCTION(LocalAlloc); + INTERCEPT_FUNCTION(LocalFree); + INTERCEPT_FUNCTION(LocalSize); + INTERCEPT_FUNCTION(LocalReAlloc); INTERCEPT_FUNCTION(HeapSize); INTERCEPT_FUNCTION(HeapFree); INTERCEPT_FUNCTION(HeapReAlloc); diff --git a/compiler-rt/lib/interception/interception_win.cc b/compiler-rt/lib/interception/interception_win.cc --- a/compiler-rt/lib/interception/interception_win.cc +++ b/compiler-rt/lib/interception/interception_win.cc @@ -531,6 +531,7 @@ case 0xec8348: // 48 83 ec XX : sub rsp, XX case 0xf88349: // 49 83 f8 XX : cmp r8, XX case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx + case 0x245489: // 89 54 24 XX : mov DWORD PTR[rsp + XX], edx return 4; case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX @@ -846,6 +847,7 @@ "msvcr120.dll", // VS2013 "vcruntime140.dll", // VS2015 "ucrtbase.dll", // Universal CRT + "KERNELBASE.dll", // KernelBase for GlobalAlloc and LocalAlloc (dynamic) // NTDLL should go last as it exports some functions that we should // override in the CRT [presumably only used internally]. "ntdll.dll", NULL}; diff --git a/compiler-rt/test/asan/TestCases/Windows/dll_host.cc b/compiler-rt/test/asan/TestCases/Windows/dll_host.cc --- a/compiler-rt/test/asan/TestCases/Windows/dll_host.cc +++ b/compiler-rt/test/asan/TestCases/Windows/dll_host.cc @@ -28,6 +28,14 @@ // IMPORT: __asan_wrap_HeapFree // IMPORT: __asan_wrap_HeapReAlloc // IMPORT: __asan_wrap_HeapSize +// IMPORT: __asan_wrap_GlobalAlloc +// IMPORT: __asan_wrap_GlobalFree +// IMPORT: __asan_wrap_GlobalReAlloc +// IMPORT: __asan_wrap_GlobalSize +// IMPORT: __asan_wrap_LocalAlloc +// IMPORT: __asan_wrap_LocalFree +// IMPORT: __asan_wrap_LocalReAlloc +// IMPORT: __asan_wrap_LocalSize // IMPORT: __asan_wrap_CreateThread // IMPORT: __asan_wrap_RaiseException // IMPORT: __asan_wrap_RtlRaiseException diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc.cc b/compiler-rt/test/asan/TestCases/Windows/globalalloc.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc.cc @@ -0,0 +1,12 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + +#include + +int main() { + char *buffer; + buffer = (char*)GlobalAlloc(GMEM_FIXED, 32), + buffer[33] = 'a'; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_doublefree.cc b/compiler-rt/test/asan/TestCases/Windows/globalalloc_doublefree.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_doublefree.cc @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + + +#include +#include + +int main(){ + void* allocation = GlobalAlloc(GMEM_FIXED, 10); + assert(allocation != 0); + assert( GlobalFree(allocation) == NULL ); + GlobalFree(allocation); //will dump + assert(0 && "GlobalFree double free should produce an ASAN dump\n" ); + return 0; +} + +// CHECK: AddressSanitizer: attempting double-free on [[addr:0x[0-9a-fA-F]+]] in thread T0: \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_flags_fallback.cc b/compiler-rt/test/asan/TestCases/Windows/globalalloc_flags_fallback.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_flags_fallback.cc @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits +#include +#include +#include + +extern "C" int +__sanitizer_get_ownership(const volatile void *p); + +int main() { + char *buffer; + HGLOBAL hMem; + hMem = GlobalAlloc(GMEM_MOVEABLE, 32); + buffer = (char*) GlobalLock(hMem); + buffer[0] = 'a'; + assert(!__sanitizer_get_ownership(buffer)); + GlobalUnlock(hMem); + GlobalFree(buffer); + puts("Okay"); + // CHECK: Okay +} diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_huge.cc b/compiler-rt/test/asan/TestCases/Windows/globalalloc_huge.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_huge.cc @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=allocator_may_return_null=true %run %t +// RUN: %env_asan_opts=allocator_may_return_null=true:windows_hook_rtl_allocators=true %run %t +// UNSUPPORTED: asan-64-bits +#include +#include +int main() { + void *nope = GlobalAlloc(GMEM_FIXED, ((size_t)0) - 1); + if (nope != nullptr) { + puts("Fail"); + return 1; + } + puts("Pass"); + return 0; +} +// CHECK: Pass +// CHECK-NOT: Fail \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_sanity.cc b/compiler-rt/test/asan/TestCases/Windows/globalalloc_sanity.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_sanity.cc @@ -0,0 +1,13 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +#include +#include + +int main() { + char *buffer; + buffer = (char*)GlobalAlloc(GMEM_FIXED, 32), + buffer[0] = 'a'; + GlobalFree(buffer); + puts("Okay"); +// CHECK: Okay +} diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_transfer.cc b/compiler-rt/test/asan/TestCases/Windows/globalalloc_transfer.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_transfer.cc @@ -0,0 +1,47 @@ +#include "sanitizer\allocator_interface.h" +#include +#include +#include +// RUN: %clang_cl_asan %s -o%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits + +int main() { + void *buffer, *realloc; + HGLOBAL hMem1, hMem2, hMem3; + + //owned by rtl + hMem1 = GlobalAlloc(GMEM_MOVEABLE, 100); + buffer = (void *) GlobalLock(hMem1); + assert(buffer); + + // still owned by rtl + hMem2 = GlobalReAlloc(buffer, 100, GMEM_MOVEABLE); + GlobalUnlock(hMem1); + buffer = (void *) GlobalLock(hMem2); + assert(buffer); + assert(!__sanitizer_get_ownership(buffer)); + assert(HeapValidate(GetProcessHeap(), 0, buffer)); + + //convert to asan owned + realloc = GlobalReAlloc(buffer, 500, GMEM_FIXED); + GlobalUnlock(hMem2); + buffer = nullptr; + assert(realloc); + assert(__sanitizer_get_ownership(realloc)); + + //convert back to rtl owned; + hMem3 = GlobalReAlloc(realloc, 100, GMEM_MOVEABLE); + buffer = (void*) GlobalLock(hMem3); + assert(buffer); + assert(!__sanitizer_get_ownership(buffer)); + assert(HeapValidate(GetProcessHeap(), 0, buffer)); + + GlobalUnlock(hMem3); + printf("Success\n"); +} + +// CHECK-NOT: assert +// CHECK-NOT: AddressSanitizer +// CHECK: Success + diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_uaf.cc b/compiler-rt/test/asan/TestCases/Windows/globalalloc_uaf.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_uaf.cc @@ -0,0 +1,13 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + +#include + +int main() { + char *buffer; + buffer = (char*)GlobalAlloc(GMEM_FIXED, 32), + GlobalFree(buffer); + buffer[0] = 'a'; +// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_zero_size.cc b/compiler-rt/test/asan/TestCases/Windows/globalalloc_zero_size.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_zero_size.cc @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan /Od -o %t %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=false %run %t 2>&1 | FileCheck %s +// RUN: %clang_cl /Od -o %t %s +// RUN: %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits +#include +#include +#include +#include + +int main() { + void *ptr = GlobalAlloc(GMEM_FIXED, 4); + assert(ptr); + void *ptr2 = GlobalReAlloc(ptr, 0, GMEM_ZEROINIT); + assert(ptr2); + GlobalFree(ptr2); + fprintf(stderr, "passed!\n"); +} + +// CHECK-NOT: double-free +// CHECK-NOT: AddressSanitizer +// CHECK: passed! \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/globalrealloc.cc b/compiler-rt/test/asan/TestCases/Windows/globalrealloc.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalrealloc.cc @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits +#include +#include +#include + +int main() { + char *oldbuf; + size_t sz = 8; + oldbuf = (char *)GlobalAlloc(GMEM_FIXED, sz); + char *newbuf = oldbuf; + while (oldbuf == newbuf) { + sz *= 2; + newbuf = (char *)GlobalReAlloc(oldbuf, sz, GMEM_ZEROINIT); + } + + newbuf[0] = 'a'; + oldbuf[0] = 'a'; + // CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] + // CHECK: WRITE of size 1 at [[WRITE2:0x[0-9a-f]+]] thread T0 + // CHECK: #0 {{0x[0-9a-f]+ in main.*}}:[[@LINE-3]] +} \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_freenull.cc b/compiler-rt/test/asan/TestCases/Windows/heapalloc_freenull.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc_freenull.cc @@ -0,0 +1,13 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 +// UNSUPPORTED: asan-64-bits +#include +#include + +int main() { + void *allocation = HeapAlloc(GetProcessHeap(), 0, 10); + assert(allocation != 0); + HeapFree(GetProcessHeap(), 0, (void *)0); +//TODO: This should actually trigger a report, since Free(NULL) is undefined for windows. + return 0; +} diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_huge.cc b/compiler-rt/test/asan/TestCases/Windows/heapalloc_huge.cc --- a/compiler-rt/test/asan/TestCases/Windows/heapalloc_huge.cc +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc_huge.cc @@ -2,6 +2,7 @@ // RUN: %env_asan_opts=allocator_may_return_null=true %run %t // RUN: %env_asan_opts=allocator_may_return_null=true:windows_hook_rtl_allocators=true %run %t // UNSUPPORTED: asan-64-bits +#include #include int main() { void *nope = HeapAlloc(GetProcessHeap(), 0, ((size_t)0) - 1); diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc.cc b/compiler-rt/test/asan/TestCases/Windows/localalloc.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc.cc @@ -0,0 +1,12 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + +#include + +int main() { + char *buffer; + buffer = (char*)LocalAlloc(LMEM_FIXED, 32), + buffer[33] = 'a'; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc_doublefree.cc b/compiler-rt/test/asan/TestCases/Windows/localalloc_doublefree.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc_doublefree.cc @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + + +#include +#include + +int main(){ + void* allocation = LocalAlloc(LMEM_FIXED, 10); + assert(allocation != 0); + assert(LocalFree(allocation) == NULL); + LocalFree(allocation); //will dump + assert(0 && "LocalFree double free should produce an ASAN dump\n" ); + return 0; +} + +// CHECK: AddressSanitizer: attempting double-free on [[addr:0x[0-9a-fA-F]+]] in thread T0: \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc_flags_fallback.cc b/compiler-rt/test/asan/TestCases/Windows/localalloc_flags_fallback.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc_flags_fallback.cc @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits +#include +#include +#include + +extern "C" int +__sanitizer_get_ownership(const volatile void *p); + +int main() { + char *buffer; + HGLOBAL hMem; + hMem = LocalAlloc(LMEM_MOVEABLE, 32); + buffer = (char*) LocalLock(hMem); + buffer[0] = 'a'; + assert(!__sanitizer_get_ownership(buffer)); + LocalUnlock(hMem); + LocalFree(buffer); + puts("Okay"); + // CHECK: Okay +} \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc_huge.cc b/compiler-rt/test/asan/TestCases/Windows/localalloc_huge.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc_huge.cc @@ -0,0 +1,9 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=allocator_may_return_null=true %run %t +// RUN: %env_asan_opts=allocator_may_return_null=true:windows_hook_rtl_allocators=true %run %t +// UNSUPPORTED: asan-64-bits +#include +int main() { + void *nope = LocalAlloc(LMEM_FIXED, ((size_t)0) - 1); + return nope != nullptr; +} \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc_sanity.cc b/compiler-rt/test/asan/TestCases/Windows/localalloc_sanity.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc_sanity.cc @@ -0,0 +1,13 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +#include +#include + +int main() { + char *buffer; + buffer = (char*)LocalAlloc(LMEM_FIXED, 32), + buffer[0] = 'a'; + LocalFree(buffer); + puts("Okay"); +// CHECK: Okay +} diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc_uaf.cc b/compiler-rt/test/asan/TestCases/Windows/localalloc_uaf.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc_uaf.cc @@ -0,0 +1,13 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + +#include + +int main() { + char *buffer; + buffer = (char*)LocalAlloc(LMEM_FIXED, 32), + LocalFree(buffer); + buffer[0] = 'a'; +// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc_zero_size.cc b/compiler-rt/test/asan/TestCases/Windows/localalloc_zero_size.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc_zero_size.cc @@ -0,0 +1,24 @@ +// RUN: %clang_cl_asan /Od -o %t %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=false %run %t 2>&1 | FileCheck %s +// RUN: %clang_cl /Od -o %t %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits +#include +#include +#include +#include + +int main() { + void *ptr = LocalAlloc(LMEM_FIXED, 4); + assert(ptr); + void *ptr2 = LocalReAlloc(ptr, 0, LMEM_ZEROINIT); + assert(ptr2); + LocalFree(ptr2); + fprintf(stderr, "passed!\n"); + +} + +// CHECK-NOT: double-free +// CHECK-NOT: AddressSanitizer +// CHECK: passed! \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/localrealloc.cc b/compiler-rt/test/asan/TestCases/Windows/localrealloc.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localrealloc.cc @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits +#include +#include +#include + +int main() { + char *oldbuf; + size_t sz = 8; + oldbuf = (char *)LocalAlloc(LMEM_FIXED, sz); + char *newbuf = oldbuf; + while (oldbuf == newbuf) { + sz *= 2; + newbuf = (char *)LocalReAlloc(oldbuf, sz, LMEM_ZEROINIT); + } + + newbuf[0] = 'a'; + oldbuf[0] = 'a'; + // CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] + // CHECK: WRITE of size 1 at [[WRITE2:0x[0-9a-f]+]] thread T0 + // CHECK: #0 {{0x[0-9a-f]+ in main.*}}:[[@LINE-3]] +} \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/localrealloc_transfer.cc b/compiler-rt/test/asan/TestCases/Windows/localrealloc_transfer.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localrealloc_transfer.cc @@ -0,0 +1,47 @@ +#include "sanitizer\allocator_interface.h" +#include +#include +#include +// RUN: %clang_cl_asan %s -o%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits + +int main() { + void *buffer, *realloc; + HLOCAL hMem1, hMem2, hMem3; + + //owned by rtl + hMem1 = LocalAlloc(LMEM_MOVEABLE, 100); + buffer = (void *) LocalLock(hMem1); + assert(buffer); + + // still owned by rtl + hMem2 = LocalReAlloc(buffer, 100, LMEM_MOVEABLE); + LocalUnlock(hMem1); + buffer = (void *) LocalLock(hMem2); + assert(buffer); + assert(!__sanitizer_get_ownership(buffer)); + assert(HeapValidate(GetProcessHeap(), 0, buffer)); + + //convert to asan owned + realloc = LocalReAlloc(buffer, 500, LMEM_FIXED); + LocalUnlock(hMem2); + buffer = nullptr; + assert(realloc); + assert(__sanitizer_get_ownership(realloc)); + + //convert back to rtl owned; + hMem3 = LocalReAlloc(realloc, 100, LMEM_MOVEABLE); + buffer = (void*) LocalLock(hMem3); + assert(buffer); + assert(!__sanitizer_get_ownership(buffer)); + assert(HeapValidate(GetProcessHeap(), 0, buffer)); + + LocalUnlock(hMem3); + printf("Success\n"); +} + +// CHECK-NOT: assert +// CHECK-NOT: AddressSanitizer +// CHECK: Success +