diff options
author | Matthew G McGovern <matthew.mcgovern@microsoft.com> | 2019-07-09 01:47:08 +0000 |
---|---|---|
committer | Matthew G McGovern <matthew.mcgovern@microsoft.com> | 2019-07-09 01:47:08 +0000 |
commit | 029fefe7e4e62cbb33cf1d0f0e7a84419d8d7a18 (patch) | |
tree | ed383723621a72f5bee0ab329e148831f959bd2f | |
parent | 4a86a652ce68eba9145170f9bc64c9c3ac776b30 (diff) | |
download | compiler-rt-029fefe7e4e62cbb33cf1d0f0e7a84419d8d7a18.tar.gz |
[sanitizers][windows] Rtl-Heap Interception and tests
- Adds interceptors for Rtl[Allocate|Free|Size|ReAllocate]Heap
- Adds unit tests for the new interceptors and expands HeapAlloc
tests to demonstrate new functionality.
Reviewed as D62927
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@365422 91177308-0d34-0410-b5e6-96231b3b80d8
25 files changed, 1081 insertions, 55 deletions
diff --git a/lib/asan/asan_flags.inc b/lib/asan/asan_flags.inc index 3c30a6283..d360e03ca 100644 --- a/lib/asan/asan_flags.inc +++ b/lib/asan/asan_flags.inc @@ -158,3 +158,5 @@ ASAN_FLAG(bool, allocator_frees_and_returns_null_on_realloc_zero, true, ASAN_FLAG(bool, verify_asan_link_order, true, "Check position of ASan runtime in library list (needs to be disabled" " when other library has to be preloaded system-wide)") +ASAN_FLAG(bool, windows_hook_rtl_allocators, false, + "(Windows only) enable hooking of Rtl(Allocate|Free|Size|ReAllocate)Heap.") diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc index b13d798a3..4586beb09 100644 --- a/lib/asan/asan_malloc_win.cc +++ b/lib/asan/asan_malloc_win.cc @@ -11,6 +11,19 @@ // Windows-specific malloc interception. //===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_allocator_interface.h" +// Need to include defintions for windows heap api functions, +// these assume windows.h will also be included. This definition +// fixes an error that's thrown if you only include heapapi.h +#if defined(_M_IX86) +#define _X86_ +#elif defined(_M_AMD64) +#define _AMD64_ +#else +#error "Missing arch or unsupported platform for Windows." +#endif +#include <heapapi.h> + #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_WINDOWS // Intentionally not including windows.h here, to avoid the risk of @@ -21,9 +34,16 @@ typedef void *HANDLE; typedef const void *LPCVOID; typedef void *LPVOID; -#define HEAP_ZERO_MEMORY 0x00000008 -#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010 - +constexpr unsigned long HEAP_ALLOCATE_SUPPORTED_FLAGS = (HEAP_ZERO_MEMORY); +constexpr unsigned long HEAP_ALLOCATE_UNSUPPORTED_FLAGS = + (~HEAP_ALLOCATE_SUPPORTED_FLAGS); +constexpr unsigned long HEAP_FREE_SUPPORTED_FLAGS = (0); +constexpr unsigned long HEAP_FREE_UNSUPPORTED_FLAGS = + (~HEAP_ALLOCATE_SUPPORTED_FLAGS); +constexpr unsigned long HEAP_REALLOC_SUPPORTED_FLAGS = + (HEAP_REALLOC_IN_PLACE_ONLY | HEAP_ZERO_MEMORY); +constexpr unsigned long HEAP_REALLOC_UNSUPPORTED_FLAGS = + (~HEAP_ALLOCATE_SUPPORTED_FLAGS); #include "asan_allocator.h" #include "asan_interceptors.h" @@ -183,45 +203,176 @@ int _CrtSetReportMode(int, int) { } } // extern "C" +#define OWNED_BY_RTL(heap, memory) \ + (!__sanitizer_get_ownership(memory) && HeapValidate(heap, 0, memory)) + +INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags, + LPCVOID lpMem) { + // If the RTL allocators are hooked we need to check whether the ASAN + // allocator owns the pointer we're about to use. Allocations occur before + // interception takes place, so if it is not owned by the RTL heap we can + // pass it to the ASAN heap for inspection. + if (flags()->windows_hook_rtl_allocators) { + if (!asan_inited || OWNED_BY_RTL(hHeap, lpMem)) + return REAL(HeapSize)(hHeap, dwFlags, lpMem); + } else { + CHECK(dwFlags == 0 && "unsupported heap flags"); + } + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(lpMem, pc, bp); +} + INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes) { + // If the ASAN runtime is not initialized, or we encounter an unsupported + // flag, fall back to the original allocator. + if (flags()->windows_hook_rtl_allocators) { + if (UNLIKELY(!asan_inited || + (dwFlags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) { + return REAL(HeapAlloc)(hHeap, dwFlags, dwBytes); + } + } else { + // In the case that we don't hook the rtl allocators, + // this becomes an assert since there is no failover to the original + // allocator. + CHECK((HEAP_ALLOCATE_UNSUPPORTED_FLAGS & dwFlags) != 0 && + "unsupported flags"); + } GET_STACK_TRACE_MALLOC; void *p = asan_malloc(dwBytes, &stack); // Reading MSDN suggests that the *entire* usable allocation is zeroed out. // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 - if (dwFlags == HEAP_ZERO_MEMORY) - internal_memset(p, 0, asan_mz_size(p)); - else - CHECK(dwFlags == 0 && "unsupported heap flags"); + if (p && (dwFlags & HEAP_ZERO_MEMORY)) { + GET_CURRENT_PC_BP_SP; + (void)sp; + auto usable_size = asan_malloc_usable_size(p, pc, bp); + internal_memset(p, 0, usable_size); + } return p; } INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) { - CHECK(dwFlags == 0 && "unsupported heap flags"); + // Heap allocations happen before this function is hooked, so we must fall + // back to the original function if the pointer is not from the ASAN heap, + // or unsupported flags are provided. + if (flags()->windows_hook_rtl_allocators) { + if (OWNED_BY_RTL(hHeap, lpMem)) + return REAL(HeapFree)(hHeap, dwFlags, lpMem); + } else { + CHECK((HEAP_FREE_UNSUPPORTED_FLAGS & dwFlags) != 0 && "unsupported flags"); + } GET_STACK_TRACE_FREE; asan_free(lpMem, &stack, FROM_MALLOC); return true; } -INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags, - LPVOID lpMem, SIZE_T dwBytes) { +namespace __asan { +using AllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, SIZE_T); +using ReAllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, LPVOID, SIZE_T); +using SizeFunction = SIZE_T(WINAPI *)(HANDLE, DWORD, LPVOID); +using FreeFunction = BOOL(WINAPI *)(HANDLE, DWORD, LPVOID); + +void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc, + FreeFunction freeFunc, AllocFunction allocFunc, + HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes) { + CHECK(reallocFunc && heapSizeFunc && freeFunc && allocFunc); GET_STACK_TRACE_MALLOC; GET_CURRENT_PC_BP_SP; (void)sp; - // Realloc should never reallocate in place. + if (flags()->windows_hook_rtl_allocators) { + enum AllocationOwnership { NEITHER = 0, ASAN = 1, RTL = 2 }; + AllocationOwnership ownershipState; + bool owned_rtlalloc = false; + bool owned_asan = __sanitizer_get_ownership(lpMem); + + if (!owned_asan) + owned_rtlalloc = HeapValidate(hHeap, 0, lpMem); + + if (owned_asan && !owned_rtlalloc) + ownershipState = ASAN; + else if (!owned_asan && owned_rtlalloc) + ownershipState = RTL; + else if (!owned_asan && !owned_rtlalloc) + ownershipState = NEITHER; + + // If this heap block which was allocated before the ASAN + // runtime came up, use the real HeapFree function. + if (UNLIKELY(!asan_inited)) { + return reallocFunc(hHeap, dwFlags, lpMem, dwBytes); + } + bool only_asan_supported_flags = + (HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) == 0; + + if (ownershipState == RTL || + (ownershipState == NEITHER && !only_asan_supported_flags)) { + if (only_asan_supported_flags) { + // if this is a conversion to ASAN upported flags, transfer this + // allocation to the ASAN allocator + void *replacement_alloc; + if (dwFlags & HEAP_ZERO_MEMORY) + replacement_alloc = asan_calloc(1, dwBytes, &stack); + else + replacement_alloc = asan_malloc(dwBytes, &stack); + if (replacement_alloc) { + size_t old_size = heapSizeFunc(hHeap, dwFlags, lpMem); + if (old_size == ((SIZE_T)0) - 1) { + asan_free(replacement_alloc, &stack, FROM_MALLOC); + return nullptr; + } + REAL(memcpy)(replacement_alloc, lpMem, old_size); + freeFunc(hHeap, dwFlags, lpMem); + } + return replacement_alloc; + } else { + // owned by rtl or neither with unsupported ASAN flags, + // just pass back to original allocator + CHECK(ownershipState == RTL || ownershipState == NEITHER); + CHECK(!only_asan_supported_flags); + return reallocFunc(hHeap, dwFlags, lpMem, dwBytes); + } + } + + if (ownershipState == ASAN && !only_asan_supported_flags) { + // Conversion to unsupported flags allocation, + // transfer this allocation back to the original allocator. + void *replacement_alloc = allocFunc(hHeap, dwFlags, dwBytes); + size_t old_usable_size = 0; + if (replacement_alloc) { + old_usable_size = asan_malloc_usable_size(lpMem, pc, bp); + REAL(memcpy)(replacement_alloc, lpMem, min(dwBytes, old_usable_size)); + asan_free(lpMem, &stack, FROM_MALLOC); + } + return replacement_alloc; + } + + CHECK((ownershipState == ASAN || ownershipState == NEITHER) && + only_asan_supported_flags); + // At this point we should either be ASAN owned with ASAN supported flags + // or we owned by neither and have supported flags. + // Pass through even when it's neither since this could be a null realloc or + // UAF that ASAN needs to catch. + } else { + CHECK((HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) != 0 && + "unsupported flags"); + } + // asan_realloc will never reallocate in place, so for now this flag is + // unsupported until we figure out a way to fake this. if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) return nullptr; - CHECK(dwFlags == 0 && "unsupported heap flags"); + // 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; @@ -231,15 +382,101 @@ INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags, if (old_size < new_size) REAL(memset)(((u8 *)ptr) + old_size, 0, new_size - old_size); } + return ptr; } +} // namespace __asan -INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags, - LPCVOID lpMem) { - CHECK(dwFlags == 0 && "unsupported heap flags"); +INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags, + LPVOID lpMem, SIZE_T dwBytes) { + return SharedReAlloc(REAL(HeapReAlloc), (SizeFunction)REAL(HeapSize), + REAL(HeapFree), REAL(HeapAlloc), hHeap, dwFlags, lpMem, + dwBytes); +} + +// The following functions are undocumented and subject to change. +// However, hooking them is necessary to hook Windows heap +// allocations with detours and their definitions are unlikely to change. +// Comments in /minkernel/ntos/rtl/heappublic.c indicate that these functions +// are part of the heap's public interface. +typedef ULONG LOGICAL; + +// This function is documented as part of the Driver Development Kit but *not* +// the Windows Development Kit. +NTSYSAPI LOGICAL RtlFreeHeap(PVOID HeapHandle, ULONG Flags, + _Frees_ptr_opt_ PVOID BaseAddress); + +// This function is documented as part of the Driver Development Kit but *not* +// the Windows Development Kit. +NTSYSAPI PVOID RtlAllocateHeap(PVOID HeapHandle, ULONG Flags, SIZE_T Size); + +// This function is completely undocumented. +PVOID +RtlReAllocateHeap(PVOID HeapHandle, ULONG Flags, PVOID BaseAddress, + SIZE_T Size); + +// This function is completely undocumented. +SIZE_T +RtlSizeHeap(PVOID HeapHandle, ULONG Flags, PVOID BaseAddress); + +INTERCEPTOR_WINAPI(SIZE_T, RtlSizeHeap, HANDLE HeapHandle, ULONG Flags, + PVOID BaseAddress) { + if (!flags()->windows_hook_rtl_allocators || + UNLIKELY(!asan_inited || OWNED_BY_RTL(HeapHandle, BaseAddress))) { + return REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress); + } GET_CURRENT_PC_BP_SP; (void)sp; - return asan_malloc_usable_size(lpMem, pc, bp); + return asan_malloc_usable_size(BaseAddress, pc, bp); +} + +INTERCEPTOR_WINAPI(BOOL, RtlFreeHeap, HANDLE HeapHandle, ULONG Flags, + PVOID BaseAddress) { + // Heap allocations happen before this function is hooked, so we must fall + // back to the original function if the pointer is not from the ASAN heap, or + // unsupported flags are provided. + if (!flags()->windows_hook_rtl_allocators || + UNLIKELY((HEAP_FREE_UNSUPPORTED_FLAGS & Flags) != 0 || + OWNED_BY_RTL(HeapHandle, BaseAddress))) { + return REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress); + } + GET_STACK_TRACE_FREE; + asan_free(BaseAddress, &stack, FROM_MALLOC); + return true; +} + +INTERCEPTOR_WINAPI(PVOID, RtlAllocateHeap, HANDLE HeapHandle, DWORD Flags, + SIZE_T Size) { + // If the ASAN runtime is not initialized, or we encounter an unsupported + // flag, fall back to the original allocator. + if (!flags()->windows_hook_rtl_allocators || + UNLIKELY(!asan_inited || + (Flags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) { + return REAL(RtlAllocateHeap)(HeapHandle, Flags, Size); + } + GET_STACK_TRACE_MALLOC; + void *p; + // Reading MSDN suggests that the *entire* usable allocation is zeroed out. + // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. + // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 + if (Flags & HEAP_ZERO_MEMORY) { + p = asan_calloc(Size, 1, &stack); + } else { + p = asan_malloc(Size, &stack); + } + return p; +} + +INTERCEPTOR_WINAPI(PVOID, RtlReAllocateHeap, HANDLE HeapHandle, ULONG Flags, + PVOID BaseAddress, SIZE_T Size) { + // If it's actually a heap block which was allocated before the ASAN runtime + // came up, use the real RtlFreeHeap function. + if (!flags()->windows_hook_rtl_allocators) + return REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size); + + return SharedReAlloc(REAL(RtlReAllocateHeap), REAL(RtlSizeHeap), + REAL(RtlFreeHeap), REAL(RtlAllocateHeap), HeapHandle, + Flags, BaseAddress, Size); } namespace __asan { @@ -272,6 +509,34 @@ void ReplaceSystemMalloc() { TryToOverrideFunction("_expand", (uptr)_expand); TryToOverrideFunction("_expand_base", (uptr)_expand); + if (flags()->windows_hook_rtl_allocators) { + INTERCEPT_FUNCTION(HeapSize); + INTERCEPT_FUNCTION(HeapFree); + INTERCEPT_FUNCTION(HeapReAlloc); + INTERCEPT_FUNCTION(HeapAlloc); + + // Undocumented functions must be intercepted by name, not by symbol. + __interception::OverrideFunction("RtlSizeHeap", (uptr)WRAP(RtlSizeHeap), + (uptr *)&REAL(RtlSizeHeap)); + __interception::OverrideFunction("RtlFreeHeap", (uptr)WRAP(RtlFreeHeap), + (uptr *)&REAL(RtlFreeHeap)); + __interception::OverrideFunction("RtlReAllocateHeap", + (uptr)WRAP(RtlReAllocateHeap), + (uptr *)&REAL(RtlReAllocateHeap)); + __interception::OverrideFunction("RtlAllocateHeap", + (uptr)WRAP(RtlAllocateHeap), + (uptr *)&REAL(RtlAllocateHeap)); + } else { +#define INTERCEPT_UCRT_FUNCTION(func) \ + if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll", \ + "api-ms-win-core-heap-l1-1-0.dll", func)) \ + VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func); + INTERCEPT_UCRT_FUNCTION(HeapAlloc); + INTERCEPT_UCRT_FUNCTION(HeapFree); + INTERCEPT_UCRT_FUNCTION(HeapReAlloc); + INTERCEPT_UCRT_FUNCTION(HeapSize); +#undef INTERCEPT_UCRT_FUNCTION + } // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which // enable cross-module inlining. This means our _malloc_base hook won't catch // all CRT allocations. This code here patches the import table of @@ -279,16 +544,8 @@ void ReplaceSystemMalloc() { // allocation API will be directed to ASan's heap. We don't currently // intercept all calls to HeapAlloc. If we did, we would have to check on // HeapFree whether the pointer came from ASan of from the system. -#define INTERCEPT_UCRT_FUNCTION(func) \ - if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll", \ - "api-ms-win-core-heap-l1-1-0.dll", func)) \ - VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func); - INTERCEPT_UCRT_FUNCTION(HeapAlloc); - INTERCEPT_UCRT_FUNCTION(HeapFree); - INTERCEPT_UCRT_FUNCTION(HeapReAlloc); - INTERCEPT_UCRT_FUNCTION(HeapSize); -#undef INTERCEPT_UCRT_FUNCTION -#endif + +#endif // defined(ASAN_DYNAMIC) } } // namespace __asan diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc index c9b81fa8f..f7601f330 100644 --- a/lib/asan/asan_win.cc +++ b/lib/asan/asan_win.cc @@ -20,10 +20,10 @@ #include "asan_interceptors.h" #include "asan_internal.h" +#include "asan_mapping.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_thread.h" -#include "asan_mapping.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_win.h" @@ -77,7 +77,7 @@ static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) { } INTERCEPTOR_WINAPI(LPTOP_LEVEL_EXCEPTION_FILTER, SetUnhandledExceptionFilter, - LPTOP_LEVEL_EXCEPTION_FILTER ExceptionFilter) { + LPTOP_LEVEL_EXCEPTION_FILTER ExceptionFilter) { CHECK(REAL(SetUnhandledExceptionFilter)); if (ExceptionFilter == &SEHHandler) return REAL(SetUnhandledExceptionFilter)(ExceptionFilter); @@ -132,7 +132,7 @@ INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { #endif static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { - AsanThread *t = (AsanThread*)arg; + AsanThread *t = (AsanThread *)arg; SetCurrentThread(t); return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr); } @@ -162,10 +162,9 @@ void InitializePlatformInterceptors() { // The interceptors were not designed to be removable, so we have to keep this // module alive for the life of the process. HMODULE pinned; - CHECK(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_PIN, - (LPCWSTR)&InitializePlatformInterceptors, - &pinned)); + CHECK(GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, + (LPCWSTR)&InitializePlatformInterceptors, &pinned)); ASAN_INTERCEPT_FUNC(CreateThread); ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter); @@ -197,6 +196,30 @@ static bool tsd_key_inited = false; static __declspec(thread) void *fake_tsd = 0; +// https://docs.microsoft.com/en-us/windows/desktop/api/winternl/ns-winternl-_teb +// "[This structure may be altered in future versions of Windows. Applications +// should use the alternate functions listed in this topic.]" +typedef struct _TEB { + PVOID Reserved1[12]; + // PVOID ThreadLocalStoragePointer; is here, at the last field in Reserved1. + PVOID ProcessEnvironmentBlock; + PVOID Reserved2[399]; + BYTE Reserved3[1952]; + PVOID TlsSlots[64]; + BYTE Reserved4[8]; + PVOID Reserved5[26]; + PVOID ReservedForOle; + PVOID Reserved6[4]; + PVOID TlsExpansionSlots; +} TEB, *PTEB; + +constexpr size_t TEB_RESERVED_FIELDS_THREAD_LOCAL_STORAGE_OFFSET = 11; +BOOL IsTlsInitialized() { + PTEB teb = (PTEB)NtCurrentTeb(); + return teb->Reserved1[TEB_RESERVED_FIELDS_THREAD_LOCAL_STORAGE_OFFSET] != + nullptr; +} + void AsanTSDInit(void (*destructor)(void *tsd)) { // FIXME: we're ignoring the destructor for now. tsd_key_inited = true; @@ -204,7 +227,7 @@ void AsanTSDInit(void (*destructor)(void *tsd)) { void *AsanTSDGet() { CHECK(tsd_key_inited); - return fake_tsd; + return IsTlsInitialized() ? fake_tsd : nullptr; } void AsanTSDSet(void *tsd) { @@ -212,9 +235,7 @@ void AsanTSDSet(void *tsd) { fake_tsd = tsd; } -void PlatformTSDDtor(void *tsd) { - AsanThread::TSDDtor(tsd); -} +void PlatformTSDDtor(void *tsd) { AsanThread::TSDDtor(tsd); } // }}} // ---------------------- Various stuff ---------------- {{{ @@ -245,9 +266,7 @@ void ReadContextStack(void *context, uptr *stack, uptr *ssize) { UNIMPLEMENTED(); } -void AsanOnDeadlySignal(int, void *siginfo, void *context) { - UNIMPLEMENTED(); -} +void AsanOnDeadlySignal(int, void *siginfo, void *context) { UNIMPLEMENTED(); } #if SANITIZER_WINDOWS64 // Exception handler for dealing with shadow memory. @@ -256,7 +275,9 @@ ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) { uptr page_size = GetPageSizeCached(); // Only handle access violations. if (exception_pointers->ExceptionRecord->ExceptionCode != - EXCEPTION_ACCESS_VIOLATION) { + EXCEPTION_ACCESS_VIOLATION || + exception_pointers->ExceptionRecord->NumberParameters < 2) { + __asan_handle_no_return(); return EXCEPTION_CONTINUE_SEARCH; } @@ -265,7 +286,10 @@ ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) { (uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]); // Check valid shadow range. - if (!AddrIsInShadow(addr)) return EXCEPTION_CONTINUE_SEARCH; + if (!AddrIsInShadow(addr)) { + __asan_handle_no_return(); + return EXCEPTION_CONTINUE_SEARCH; + } // This is an access violation while trying to read from the shadow. Commit // the relevant page and let execution continue. @@ -276,7 +300,8 @@ ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) { // Commit the page. uptr result = (uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE); - if (result != page) return EXCEPTION_CONTINUE_SEARCH; + if (result != page) + return EXCEPTION_CONTINUE_SEARCH; // The page mapping succeeded, so continue execution as usual. return EXCEPTION_CONTINUE_EXECUTION; @@ -293,7 +318,7 @@ void InitializePlatformExceptionHandlers() { } bool IsSystemHeapAddress(uptr addr) { - return ::HeapValidate(GetProcessHeap(), 0, (void*)addr) != FALSE; + return ::HeapValidate(GetProcessHeap(), 0, (void *)addr) != FALSE; } // We want to install our own exception handler (EH) to print helpful reports @@ -312,8 +337,7 @@ bool IsSystemHeapAddress(uptr addr) { // asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter // will be called for each instrumented module. This ensures that at least one // __asan_set_seh_filter call happens after the .exe module CRT is initialized. -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -int __asan_set_seh_filter() { +extern "C" SANITIZER_INTERFACE_ATTRIBUTE int __asan_set_seh_filter() { // We should only store the previous handler if it's not our own handler in // order to avoid loops in the EH chain. auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler); @@ -347,12 +371,13 @@ __declspec(allocate(".CRT$XCAB")) int (*__intercept_seh)() = // which run before the CRT. Users also add code to .CRT$XLC, so it's important // to run our initializers first. static void NTAPI asan_thread_init(void *module, DWORD reason, void *reserved) { - if (reason == DLL_PROCESS_ATTACH) __asan_init(); + if (reason == DLL_PROCESS_ATTACH) + __asan_init(); } #pragma section(".CRT$XLAB", long, read) // NOLINT -__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *, - unsigned long, void *) = asan_thread_init; +__declspec(allocate(".CRT$XLAB")) void(NTAPI *__asan_tls_init)( + void *, unsigned long, void *) = asan_thread_init; #endif static void NTAPI asan_thread_exit(void *module, DWORD reason, void *reserved) { @@ -365,8 +390,8 @@ static void NTAPI asan_thread_exit(void *module, DWORD reason, void *reserved) { } #pragma section(".CRT$XLY", long, read) // NOLINT -__declspec(allocate(".CRT$XLY")) void (NTAPI *__asan_tls_exit)(void *, - unsigned long, void *) = asan_thread_exit; +__declspec(allocate(".CRT$XLY")) void(NTAPI *__asan_tls_exit)( + void *, unsigned long, void *) = asan_thread_exit; WIN_FORCE_LINK(__asan_dso_reg_hook) diff --git a/test/asan/TestCases/Windows/dll_host.cc b/test/asan/TestCases/Windows/dll_host.cc index 512f930bc..9a4181541 100644 --- a/test/asan/TestCases/Windows/dll_host.cc +++ b/test/asan/TestCases/Windows/dll_host.cc @@ -32,6 +32,10 @@ // IMPORT: __asan_wrap_RaiseException // IMPORT: __asan_wrap_RtlRaiseException // IMPORT: __asan_wrap_SetUnhandledExceptionFilter +// IMPORT: __asan_wrap_RtlSizeHeap +// IMPORT: __asan_wrap_RtlAllocateHeap +// IMPORT: __asan_wrap_RtlReAllocateHeap +// IMPORT: __asan_wrap_RtlFreeHeap // // RUN: cat %t.imports1 %t.imports2 | sort | uniq > %t.imports-sorted // RUN: cat %t.exports1 %t.exports2 | sort | uniq > %t.exports-sorted diff --git a/test/asan/TestCases/Windows/dll_unload.cc b/test/asan/TestCases/Windows/dll_unload.cc new file mode 100644 index 000000000..055567a0f --- /dev/null +++ b/test/asan/TestCases/Windows/dll_unload.cc @@ -0,0 +1,51 @@ +#include <stdio.h> +#include <windows.h> + +// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll +// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %te.exe %t.dll 2>&1 | FileCheck %s +// REQUIRES: asan-dynamic-runtime +// REQUIRES: asan-32-bits + +#include <cassert> +#include <stdio.h> +#include <windows.h> +extern "C" { +#if defined(EXE) + +int main(int argc, char **argv) { + void *region_without_hooks = HeapAlloc(GetProcessHeap(), 0, 10); + HMODULE lib = LoadLibraryA(argv[1]); + assert(lib != INVALID_HANDLE_VALUE); + + void *region_w_hooks = HeapAlloc(GetProcessHeap(), 0, 10); + assert(region_w_hooks != nullptr); + assert(0 != FreeLibrary(lib)); + + fprintf(stderr, "WITHOUT:0x%08x\n", (unsigned int)region_without_hooks); + fprintf(stderr, "WITH:0x%08x\n", (unsigned int)region_w_hooks); + + assert(0 != HeapFree(GetProcessHeap(), 0, region_without_hooks)); + assert(0 != HeapFree(GetProcessHeap(), 0, region_w_hooks)); + + HeapFree(GetProcessHeap(), 0, region_w_hooks); //will dump +} +#elif defined(DLL) +// This global is registered at startup. + +BOOL WINAPI DllMain(HMODULE, DWORD reason, LPVOID) { + fprintf(stderr, "in DLL(reason=%d)\n", (int)reason); + fflush(0); + return TRUE; +} + +// CHECK: in DLL(reason=1) +// CHECK: in DLL(reason=0) +// CHECK: WITHOUT:[[WITHOUT:0x[0-9a-fA-F]+]] +// CHECK: WITH:[[WITH:0x[0-9a-fA-F]+]] +// CHECK: AddressSanitizer: attempting double-free on [[WITH]] in thread T0: + +#else +#error oops! +#endif +} diff --git a/test/asan/TestCases/Windows/heapalloc.cc b/test/asan/TestCases/Windows/heapalloc.cc new file mode 100644 index 000000000..66322a3e7 --- /dev/null +++ b/test/asan/TestCases/Windows/heapalloc.cc @@ -0,0 +1,13 @@ +// XFAIL: asan-64-bits +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + +#include <windows.h> + +int main() { + char *buffer; + buffer = (char *)HeapAlloc(GetProcessHeap(), 0, 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/test/asan/TestCases/Windows/heapalloc_dll_double_free.cc b/test/asan/TestCases/Windows/heapalloc_dll_double_free.cc new file mode 100644 index 000000000..496289ae1 --- /dev/null +++ b/test/asan/TestCases/Windows/heapalloc_dll_double_free.cc @@ -0,0 +1,40 @@ +#include <stdio.h> +#include <windows.h> + +// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll +// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %te.exe %t.dll 2>&1 | FileCheck %s +// REQUIRES: asan-dynamic-runtime +// REQUIRES: asan-32-bits + +#include <cassert> +#include <stdio.h> +#include <windows.h> +extern "C" { +#if defined(EXE) + +int main(int argc, char **argv) { + void *region_without_hooks = HeapAlloc(GetProcessHeap(), 0, 10); + HMODULE lib = LoadLibraryA(argv[1]); + assert(lib != INVALID_HANDLE_VALUE); + assert(0 != FreeLibrary(lib)); + assert(0 != HeapFree(GetProcessHeap(), 0, region_without_hooks)); + assert(0 != HeapFree(GetProcessHeap(), 0, region_without_hooks)); +} +#elif defined(DLL) +// This global is registered at startup. + +BOOL WINAPI DllMain(HMODULE, DWORD reason, LPVOID) { + fprintf(stderr, "in DLL(reason=%d)\n", (int)reason); + fflush(0); + return TRUE; +} + +// CHECK: in DLL(reason=1) +// CHECK: in DLL(reason=0) +// CHECK: AddressSanitizer: nested bug in the same thread, aborting. + +#else +#error oops! +#endif +} diff --git a/test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cc b/test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cc new file mode 100644 index 000000000..79b30f4a9 --- /dev/null +++ b/test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cc @@ -0,0 +1,40 @@ +#include <stdio.h> +#include <windows.h> + +// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll +// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %te.exe %t.dll 2>&1 | FileCheck %s +// REQUIRES: asan-dynamic-runtime +// REQUIRES: asan-32-bits + +#include <cassert> +#include <stdio.h> +#include <windows.h> +extern "C" { +#if defined(EXE) + +int main(int argc, char **argv) { + void *region_without_hooks = HeapAlloc(GetProcessHeap(), 0, 10); + HMODULE lib = LoadLibraryA(argv[1]); + assert(lib != INVALID_HANDLE_VALUE); + assert(0 != FreeLibrary(lib)); + assert(0 != HeapFree(GetProcessHeap(), 0, region_without_hooks)); + HeapReAlloc(GetProcessHeap(), 0, region_without_hooks, 100); //should throw nested error +} +#elif defined(DLL) +// This global is registered at startup. + +BOOL WINAPI DllMain(HMODULE, DWORD reason, LPVOID) { + fprintf(stderr, "in DLL(reason=%d)\n", (int)reason); + fflush(0); + return TRUE; +} + +// CHECK: in DLL(reason=1) +// CHECK: in DLL(reason=0) +// CHECK: AddressSanitizer: nested bug in the same thread, aborting. + +#else +#error oops! +#endif +} diff --git a/test/asan/TestCases/Windows/heapalloc_doublefree.cc b/test/asan/TestCases/Windows/heapalloc_doublefree.cc new file mode 100644 index 000000000..dcdf98932 --- /dev/null +++ b/test/asan/TestCases/Windows/heapalloc_doublefree.cc @@ -0,0 +1,16 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits +#include <cassert> +#include <windows.h> + +int main() { + void *allocation = HeapAlloc(GetProcessHeap(), 0, 10); + assert(allocation != 0); + assert(HeapFree(GetProcessHeap(), 0, allocation)); + HeapFree(GetProcessHeap(), 0, allocation); //will dump + assert(0 && "HeapFree 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/test/asan/TestCases/Windows/heapalloc_flags_fallback.cc b/test/asan/TestCases/Windows/heapalloc_flags_fallback.cc new file mode 100644 index 000000000..eda94ac00 --- /dev/null +++ b/test/asan/TestCases/Windows/heapalloc_flags_fallback.cc @@ -0,0 +1,20 @@ +// RUN: %clang_cl_asan -O0 %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 +// XFAIL: asan-64-bits +#include <assert.h> +#include <stdio.h> +#include <windows.h> + +extern "C" int +__sanitizer_get_ownership(const volatile void *p); + +int main() { + char *buffer; + buffer = (char *)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, 32); + buffer[0] = 'a'; + assert(!__sanitizer_get_ownership(buffer)); + HeapFree(GetProcessHeap(), 0, buffer); + puts("Okay"); + // CHECK: Okay +} diff --git a/test/asan/TestCases/Windows/heapalloc_huge.cc b/test/asan/TestCases/Windows/heapalloc_huge.cc new file mode 100644 index 000000000..ce1fd6562 --- /dev/null +++ b/test/asan/TestCases/Windows/heapalloc_huge.cc @@ -0,0 +1,9 @@ +// RUN: %clang_cl_asan -O0 %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 +// XFAIL: asan-64-bits +#include <windows.h> +int main() { + void *nope = HeapAlloc(GetProcessHeap(), 0, ((size_t)0) - 1); + return (nope == nullptr) ? 0 : 1; +}
\ No newline at end of file diff --git a/test/asan/TestCases/Windows/heapalloc_rtl_transfer.cc b/test/asan/TestCases/Windows/heapalloc_rtl_transfer.cc new file mode 100644 index 000000000..b621270d7 --- /dev/null +++ b/test/asan/TestCases/Windows/heapalloc_rtl_transfer.cc @@ -0,0 +1,98 @@ +#include "sanitizer\allocator_interface.h" +#include <cassert> +#include <stdio.h> +#include <windows.h> + +// RUN: %clang_cl_asan %s -o%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits + +using AllocateFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, SIZE_T); +using ReAllocateFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, PVOID, SIZE_T); + +using FreeFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, PVOID); + +int main() { + HMODULE NtDllHandle = GetModuleHandle("ntdll.dll"); + if (!NtDllHandle) { + puts("Couldn't load ntdll??"); + return -1; + } + + auto RtlAllocateHeap_ptr = (AllocateFunctionPtr)GetProcAddress(NtDllHandle, "RtlAllocateHeap"); + if (RtlAllocateHeap_ptr == 0) { + puts("Couldn't find RtlAllocateHeap"); + return -1; + } + + auto RtlReAllocateHeap_ptr = (ReAllocateFunctionPtr)GetProcAddress(NtDllHandle, "RtlReAllocateHeap"); + if (RtlReAllocateHeap_ptr == 0) { + puts("Couldn't find RtlReAllocateHeap"); + return -1; + } + + //owned by rtl + void *alloc = RtlAllocateHeap_ptr(GetProcessHeap(), + HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, 100); + assert(alloc); + for (int i = 0; i < 100; i++) { + assert(((char *)alloc)[i] == 0); + ((char *)alloc)[i] = '\xcc'; + } + + // still owned by rtl + alloc = RtlReAllocateHeap_ptr(GetProcessHeap(), + HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, alloc, 500); + assert(alloc && !__sanitizer_get_ownership(alloc) && HeapValidate(GetProcessHeap(), 0, alloc)); + for (int i = 0; i < 100; i++) { + assert(((char *)alloc)[i] == '\xcc'); + } + for (int i = 100; i < 500; i++) { + assert(((char *)alloc)[i] == 0); + ((char *)alloc)[i] = '\xcc'; + } + + //convert to asan owned + void *realloc = RtlReAllocateHeap_ptr(GetProcessHeap(), + HEAP_ZERO_MEMORY, alloc, 600); + alloc = nullptr; + assert(realloc && __sanitizer_get_ownership(realloc)); + + for (int i = 0; i < 500; i++) { + assert(((char *)realloc)[i] == '\xcc'); + } + for (int i = 500; i < 600; i++) { + assert(((char *)realloc)[i] == 0); + ((char *)realloc)[i] = '\xcc'; + } + realloc = RtlReAllocateHeap_ptr(GetProcessHeap(), + HEAP_ZERO_MEMORY, realloc, 2048); + assert(realloc && __sanitizer_get_ownership(realloc)); + + for (int i = 0; i < 600; i++) { + assert(((char *)realloc)[i] == '\xcc'); + } + for (int i = 600; i < 2048; i++) { + assert(((char *)realloc)[i] == 0); + ((char *)realloc)[i] = '\xcc'; + } + //convert back to rtl owned; + alloc = RtlReAllocateHeap_ptr(GetProcessHeap(), + HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, realloc, 100); + assert(alloc && !__sanitizer_get_ownership(alloc) && HeapValidate(GetProcessHeap(), 0, alloc)); + for (int i = 0; i < 100; i++) { + assert(((char *)alloc)[i] == '\xcc'); + ((char *)alloc)[i] = 0; + } + + auto usable_size = HeapSize(GetProcessHeap(), 0, alloc); + for (int i = 100; i < usable_size; i++) { + assert(((char *)alloc)[i] == 0); + } + + printf("Success\n"); +} + +// CHECK-NOT: Assertion failed: +// CHECK-NOT: AddressSanitizer +// CHECK: Success
\ No newline at end of file diff --git a/test/asan/TestCases/Windows/heapalloc_sanity.cc b/test/asan/TestCases/Windows/heapalloc_sanity.cc new file mode 100644 index 000000000..5dfc14b72 --- /dev/null +++ b/test/asan/TestCases/Windows/heapalloc_sanity.cc @@ -0,0 +1,13 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <windows.h> + +int main() { + char *buffer; + buffer = (char *)HeapAlloc(GetProcessHeap(), 0, 32), + buffer[0] = 'a'; + HeapFree(GetProcessHeap(), 0, buffer); + puts("Okay"); + // CHECK: Okay +} diff --git a/test/asan/TestCases/Windows/heapalloc_transfer.cc b/test/asan/TestCases/Windows/heapalloc_transfer.cc new file mode 100644 index 000000000..b3b5746d6 --- /dev/null +++ b/test/asan/TestCases/Windows/heapalloc_transfer.cc @@ -0,0 +1,28 @@ +#include "sanitizer\allocator_interface.h" +#include <cassert> +#include <stdio.h> +#include <windows.h> +// RUN: %clang_cl_asan %s -o%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits + +int main() { + //owned by rtl + void *alloc = HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, 100); + assert(alloc); + // still owned by rtl + alloc = HeapReAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, alloc, 100); + assert(alloc && !__sanitizer_get_ownership(alloc) && HeapValidate(GetProcessHeap(), 0, alloc)); + //convert to asan owned + void *realloc = HeapReAlloc(GetProcessHeap(), 0, alloc, 500); + alloc = nullptr; + assert(realloc && __sanitizer_get_ownership(realloc)); + //convert back to rtl owned; + alloc = HeapReAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, realloc, 100); + assert(alloc && !__sanitizer_get_ownership(alloc) && HeapValidate(GetProcessHeap(), 0, alloc)); + printf("Success\n"); +} + +// CHECK-NOT: assert +// CHECK-NOT: AddressSanitizer +// CHECK: Success
\ No newline at end of file diff --git a/test/asan/TestCases/Windows/heapalloc_uaf.cc b/test/asan/TestCases/Windows/heapalloc_uaf.cc new file mode 100644 index 000000000..00fb3ab6f --- /dev/null +++ b/test/asan/TestCases/Windows/heapalloc_uaf.cc @@ -0,0 +1,13 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits +#include <windows.h> + +int main() { + char *buffer; + buffer = (char *)HeapAlloc(GetProcessHeap(), 0, 32), + HeapFree(GetProcessHeap(), 0, 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/test/asan/TestCases/Windows/heapalloc_zero_size.cc b/test/asan/TestCases/Windows/heapalloc_zero_size.cc new file mode 100644 index 000000000..23fc97b20 --- /dev/null +++ b/test/asan/TestCases/Windows/heapalloc_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 +// XFAIL: asan-64-bits +#include <cassert> +#include <stdio.h> +#include <windows.h> + +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! diff --git a/test/asan/TestCases/Windows/heaprealloc.cc b/test/asan/TestCases/Windows/heaprealloc.cc new file mode 100644 index 000000000..e6efb87b5 --- /dev/null +++ b/test/asan/TestCases/Windows/heaprealloc.cc @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits +#include <stdio.h> +#include <windows.h> + +int main() { + char *oldbuf; + size_t sz = 8; + HANDLE procHeap = GetProcessHeap(); + oldbuf = (char *)HeapAlloc(procHeap, 0, sz); + char *newbuf = oldbuf; + while (oldbuf == newbuf) { + sz *= 2; + newbuf = (char *)HeapReAlloc(procHeap, 0, oldbuf, sz); + } + + 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]] +} diff --git a/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cc b/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cc new file mode 100644 index 000000000..76ae89690 --- /dev/null +++ b/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cc @@ -0,0 +1,58 @@ +// RUN: %clang_cl_asan /Od /MT -o %t %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits +#include <cassert> +#include <iostream> +#include <windows.h> + +int main() { + void *ptr = malloc(0); + if (ptr) + std::cerr << "allocated!\n"; + ((char *)ptr)[0] = '\xff'; //check this 'allocate 1 instead of 0' hack hasn't changed + + free(ptr); + + /* + HeapAlloc hack for our asan interceptor is to change 0 + sized allocations to size 1 to avoid weird inconsistencies + between how realloc and heaprealloc handle 0 size allocations. + + Note this test relies on these instructions being intercepted. + Without ASAN HeapRealloc on line 27 would return a ptr whose + HeapSize would be 0. This test makes sure that the underlying behavior + of our hack hasn't changed underneath us. + + We can get rid of the test (or change it to test for the correct + behavior) once we fix the interceptor or write a different allocator + to handle 0 sized allocations properly by default. + + */ + ptr = HeapAlloc(GetProcessHeap(), 0, 0); + if (!ptr) + return 1; + void *ptr2 = HeapReAlloc(GetProcessHeap(), 0, ptr, 0); + if (!ptr2) + return 1; + size_t heapsize = HeapSize(GetProcessHeap(), 0, ptr2); + if (heapsize != 1) { // will be 0 without ASAN turned on + std::cerr << "HeapAlloc size failure! " << heapsize << " != 1\n"; + return 1; + } + void *ptr3 = HeapReAlloc(GetProcessHeap(), 0, ptr2, 3); + if (!ptr3) + return 1; + heapsize = HeapSize(GetProcessHeap(), 0, ptr3); + + if (heapsize != 3) { + std::cerr << "HeapAlloc size failure! " << heapsize << " != 3\n"; + return 1; + } + HeapFree(GetProcessHeap(), 0, ptr3); + return 0; +} + +// CHECK: allocated! +// CHECK-NOT: heap-buffer-overflow +// CHECK-NOT: AddressSanitizer +// CHECK-NOT: HeapAlloc size failure!
\ No newline at end of file diff --git a/test/asan/TestCases/Windows/heaprealloc_zero_size.cc b/test/asan/TestCases/Windows/heaprealloc_zero_size.cc index 3d60202ce..ad2478b19 100644 --- a/test/asan/TestCases/Windows/heaprealloc_zero_size.cc +++ b/test/asan/TestCases/Windows/heaprealloc_zero_size.cc @@ -1,7 +1,9 @@ // RUN: %clang_cl_asan /Od -o %t %s -// RUN: %run %t 2>&1 | FileCheck %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 +// XFAIL: asan-64-bits #include <cassert> #include <stdio.h> #include<windows.h> diff --git a/test/asan/TestCases/Windows/queue_user_work_item_report.cc b/test/asan/TestCases/Windows/queue_user_work_item_report.cc index 26bd5e09d..27c438a56 100644 --- a/test/asan/TestCases/Windows/queue_user_work_item_report.cc +++ b/test/asan/TestCases/Windows/queue_user_work_item_report.cc @@ -9,9 +9,9 @@ DWORD CALLBACK work_item(LPVOID) { int subscript = -1; volatile char stack_buffer[42]; stack_buffer[subscript] = 42; -// CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]] -// CHECK: WRITE of size 1 at [[ADDR]] thread T1 -// CHECK: {{#0 .* work_item.*queue_user_work_item_report.cc}}:[[@LINE-3]] + // CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]] + // CHECK: WRITE of size 1 at [[ADDR]] thread T{{[0-9]+}} + // CHECK: {{#0 .* work_item.*queue_user_work_item_report.cc}}:[[@LINE-3]] SetEvent(done); return 0; } diff --git a/test/asan/TestCases/Windows/rtlallocateheap.cc b/test/asan/TestCases/Windows/rtlallocateheap.cc new file mode 100644 index 000000000..5831fe27b --- /dev/null +++ b/test/asan/TestCases/Windows/rtlallocateheap.cc @@ -0,0 +1,30 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t /MD +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits +// REQUIRES: asan-rtl-heap-interception + +#include <stdio.h> +#include <windows.h> + +using AllocateFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, SIZE_T); +using FreeFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, PVOID); + +int main() { + HMODULE NtDllHandle = GetModuleHandle("ntdll.dll"); + if (!NtDllHandle) { + puts("Couldn't load ntdll??"); + return -1; + } + + auto RtlAllocateHeap_ptr = (AllocateFunctionPtr)GetProcAddress(NtDllHandle, "RtlAllocateHeap"); + if (RtlAllocateHeap_ptr == 0) { + puts("Couldn't RtlAllocateHeap"); + return -1; + } + + char *buffer; + buffer = (char *)RtlAllocateHeap_ptr(GetProcessHeap(), 0, 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/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_double_free.cc b/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_double_free.cc new file mode 100644 index 000000000..078114506 --- /dev/null +++ b/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_double_free.cc @@ -0,0 +1,72 @@ +// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll +// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %te.exe %t.dll 2>&1 | FileCheck %s +// REQUIRES: asan-dynamic-runtime +// REQUIRES: asan-32-bits +// REQUIRES: asan-rtl-heap-interception + +#include <cassert> +#include <stdio.h> +#include <windows.h> + +extern "C" { +#if defined(EXE) +using AllocateFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, SIZE_T); +using FreeFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, PVOID); + +int main(int argc, char **argv) { + HMODULE NtDllHandle = GetModuleHandle("ntdll.dll"); + if (!NtDllHandle) { + puts("Couldn't load ntdll??"); + return -1; + } + + auto RtlAllocateHeap_ptr = + (AllocateFunctionPtr)GetProcAddress(NtDllHandle, "RtlAllocateHeap"); + if (RtlAllocateHeap_ptr == 0) { + puts("Couldn't RtlAllocateHeap"); + return -1; + } + + auto RtlFreeHeap_ptr = + (FreeFunctionPtr)GetProcAddress(NtDllHandle, "RtlFreeHeap"); + if (RtlFreeHeap_ptr == 0) { + puts("Couldn't get RtlFreeHeap"); + return -1; + } + + char *buffer; + buffer = (char *)RtlAllocateHeap_ptr(GetProcessHeap(), 0, 32); + + HMODULE lib = LoadLibraryA(argv[1]); + assert(lib != INVALID_HANDLE_VALUE); + assert(0 != FreeLibrary(lib)); + + if (!RtlFreeHeap_ptr(GetProcessHeap(), 0, buffer)) { + puts("Couldn't RtlFreeHeap"); + return -1; + } + // Because this pointer was allocated pre-hooking, + // this will dump as a nested bug. Asan attempts to free + // the pointer and AV's, so the ASAN exception handler + // will dump as a 'nested bug'. + RtlFreeHeap_ptr(GetProcessHeap(), 0, buffer); +} + +#elif defined(DLL) +// This global is registered at startup. + +BOOL WINAPI DllMain(HMODULE, DWORD reason, LPVOID) { + fprintf(stderr, "in DLL(reason=%d)\n", (int)reason); + fflush(0); + return TRUE; +} + +// CHECK: in DLL(reason=1) +// CHECK: in DLL(reason=0) +// CHECK: AddressSanitizer: nested bug in the same thread, aborting. + +#else +#error oops! +#endif +} diff --git a/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_realloc.cc b/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_realloc.cc new file mode 100644 index 000000000..a2758cad2 --- /dev/null +++ b/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_realloc.cc @@ -0,0 +1,76 @@ +// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll +// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %te.exe %t.dll 2>&1 | FileCheck %s +// REQUIRES: asan-dynamic-runtime +// REQUIRES: asan-32-bits +// REQUIRES: asan-rtl-heap-interception + +#include <cassert> +#include <stdio.h> +#include <windows.h> + +extern "C" { +#if defined(EXE) +using AllocateFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, SIZE_T); +using FreeFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, PVOID); +using RtlReAllocateHeapPtr = PVOID(__stdcall *)(PVOID, ULONG, PVOID, SIZE_T); + +int main(int argc, char **argv) { + HMODULE NtDllHandle = GetModuleHandle("ntdll.dll"); + if (!NtDllHandle) { + puts("Couldn't load ntdll??"); + return -1; + } + + auto RtlAllocateHeap_ptr = + (AllocateFunctionPtr)GetProcAddress(NtDllHandle, "RtlAllocateHeap"); + if (RtlAllocateHeap_ptr == 0) { + puts("Couldn't RtlAllocateHeap"); + return -1; + } + + auto RtlFreeHeap_ptr = + (FreeFunctionPtr)GetProcAddress(NtDllHandle, "RtlFreeHeap"); + if (RtlFreeHeap_ptr == 0) { + puts("Couldn't get RtlFreeHeap"); + return -1; + } + + auto RtlReAllocateHeap_ptr = + (RtlReAllocateHeapPtr)GetProcAddress(NtDllHandle, "RtlReAllocateHeap"); + if (RtlReAllocateHeap_ptr == 0) { + puts("Couldn't get rtlreallocateheap\n"); + return -1; + } + + char *buffer; + buffer = (char *)RtlAllocateHeap_ptr(GetProcessHeap(), 0, 32); + + HMODULE lib = LoadLibraryA(argv[1]); + assert(lib != INVALID_HANDLE_VALUE); + assert(0 != FreeLibrary(lib)); + + if (!RtlFreeHeap_ptr(GetProcessHeap(), 0, buffer)) { + puts("Couldn't RtlFreeHeap"); + return -1; + } + RtlReAllocateHeap_ptr(GetProcessHeap(), 0, buffer, 100); // should dump +} + +#elif defined(DLL) +// This global is registered at startup. + +BOOL WINAPI DllMain(HMODULE, DWORD reason, LPVOID) { + fprintf(stderr, "in DLL(reason=%d)\n", (int)reason); + fflush(0); + return TRUE; +} + +// CHECK: in DLL(reason=1) +// CHECK: in DLL(reason=0) +// CHECK: AddressSanitizer: nested bug in the same thread, aborting. + +#else +#error oops! +#endif +} diff --git a/test/asan/TestCases/Windows/rtlallocateheap_flags_fallback.cc b/test/asan/TestCases/Windows/rtlallocateheap_flags_fallback.cc new file mode 100644 index 000000000..c377fe360 --- /dev/null +++ b/test/asan/TestCases/Windows/rtlallocateheap_flags_fallback.cc @@ -0,0 +1,45 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t /MD +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits +// REQUIRES: asan-rtl-heap-interception + +#include <assert.h> +#include <stdio.h> +#include <windows.h> + +extern "C" int __sanitizer_get_ownership(const volatile void *p); +using AllocateFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, SIZE_T); +using FreeFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, PVOID); + +int main() { + HMODULE NtDllHandle = GetModuleHandle("ntdll.dll"); + if (!NtDllHandle) { + puts("Couldn't load ntdll??"); + return -1; + } + + auto RtlAllocateHeap_ptr = (AllocateFunctionPtr)GetProcAddress(NtDllHandle, "RtlAllocateHeap"); + if (RtlAllocateHeap_ptr == 0) { + puts("Couldn't RtlAllocateHeap"); + return -1; + } + + auto RtlFreeHeap_ptr = (FreeFunctionPtr)GetProcAddress(NtDllHandle, "RtlFreeHeap"); + if (RtlFreeHeap_ptr == 0) { + puts("Couldn't RtlFreeHeap"); + return -1; + } + + char *winbuf; + char *asanbuf; + winbuf = (char *)RtlAllocateHeap_ptr(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, 32), + asanbuf = (char *)RtlAllocateHeap_ptr(GetProcessHeap(), 0, 32), + winbuf[0] = 'a'; + assert(!__sanitizer_get_ownership(winbuf)); + assert(__sanitizer_get_ownership(asanbuf)); + + RtlFreeHeap_ptr(GetProcessHeap(), 0, winbuf); + RtlFreeHeap_ptr(GetProcessHeap(), 0, asanbuf); + puts("Okay"); + // CHECK: Okay +} diff --git a/test/asan/TestCases/Windows/rtlallocateheap_zero.cc b/test/asan/TestCases/Windows/rtlallocateheap_zero.cc new file mode 100644 index 000000000..d4bde7857 --- /dev/null +++ b/test/asan/TestCases/Windows/rtlallocateheap_zero.cc @@ -0,0 +1,68 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t /MD +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits +// REQUIRES: asan-rtl-heap-interception + +#include <assert.h> +#include <stdio.h> +#include <windows.h> + +using AllocateFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, SIZE_T); +using ReAllocateFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, PVOID, SIZE_T); +using FreeFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, PVOID); + +int main() { + HMODULE NtDllHandle = GetModuleHandle("ntdll.dll"); + if (!NtDllHandle) { + puts("Couldn't load ntdll??"); + return -1; + } + + auto RtlAllocateHeap_ptr = (AllocateFunctionPtr)GetProcAddress(NtDllHandle, "RtlAllocateHeap"); + if (RtlAllocateHeap_ptr == 0) { + puts("Couldn't find RtlAllocateHeap"); + return -1; + } + + auto RtlReAllocateHeap_ptr = (ReAllocateFunctionPtr)GetProcAddress(NtDllHandle, "RtlReAllocateHeap"); + if (RtlReAllocateHeap_ptr == 0) { + puts("Couldn't find RtlReAllocateHeap"); + return -1; + } + + char *buffer; + SIZE_T buffer_size = 32; + SIZE_T new_buffer_size = buffer_size * 2; + + buffer = (char *)RtlAllocateHeap_ptr(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_size); + assert(buffer != nullptr); + // Check that the buffer is zeroed. + for (SIZE_T i = 0; i < buffer_size; ++i) { + assert(buffer[i] == 0); + } + memset(buffer, 0xcc, buffer_size); + + // Zero the newly allocated memory. + buffer = (char *)RtlReAllocateHeap_ptr(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer, new_buffer_size); + assert(buffer != nullptr); + // Check that the first part of the buffer still has the old contents. + for (SIZE_T i = 0; i < buffer_size; ++i) { + assert(buffer[i] == (char)0xcc); + } + // Check that the new part of the buffer is zeroed. + for (SIZE_T i = buffer_size; i < new_buffer_size; ++i) { + assert(buffer[i] == 0x0); + } + + // Shrink the buffer back down. + buffer = (char *)RtlReAllocateHeap_ptr(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer, buffer_size); + assert(buffer != nullptr); + // Check that the first part of the buffer still has the old contents. + for (SIZE_T i = 0; i < buffer_size; ++i) { + assert(buffer[i] == (char)0xcc); + } + + buffer[buffer_size + 1] = 'a'; + // CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] + // CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} |