Index: .gitignore =================================================================== --- .gitignore +++ .gitignore @@ -45,7 +45,9 @@ /CMakeSettings.json # CLion project configuration /.idea +#azure pipelines build config +.azure-pipelines.yml #==============================================================================# # Directories to ignore (do not add trailing '/'s, they skip symlinks). #==============================================================================# Index: compiler-rt/lib/asan/asan_flags.inc =================================================================== --- compiler-rt/lib/asan/asan_flags.inc +++ compiler-rt/lib/asan/asan_flags.inc @@ -158,3 +158,5 @@ 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.") 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 @@ -11,6 +11,21 @@ // 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 + + #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_WINDOWS // Intentionally not including windows.h here, to avoid the risk of @@ -24,22 +39,29 @@ #define HEAP_ZERO_MEMORY 0x00000008 #define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010 +#define HEAP_ALLOCATE_SUPPORTED_FLAGS (HEAP_ZERO_MEMORY) +#define HEAP_ALLOCATE_UNSUPPORTED_FLAGS (~HEAP_ALLOCATE_SUPPORTED_FLAGS) +#define HEAP_FREE_SUPPORTED_FLAGS (0) +#define HEAP_FREE_UNSUPPORTED_FLAGS (~HEAP_ALLOCATE_SUPPORTED_FLAGS) +#define HEAP_REALLOC_SUPPORTED_FLAGS \ + (HEAP_REALLOC_IN_PLACE_ONLY | HEAP_ZERO_MEMORY) +#define HEAP_REALLOC_UNSUPPORTED_FLAGS (~HEAP_ALLOCATE_SUPPORTED_FLAGS) #include "asan_allocator.h" #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_stack.h" #include "interception/interception.h" #include using namespace __asan; // NOLINT // MT: Simply defining functions with the same signature in *.obj // files overrides the standard functions in the CRT. // MD: Memory allocation functions are defined in the CRT .dll, // so we have to intercept them before they are called for the first time. #if ASAN_DYNAMIC # define ALLOCATION_FUNCTION_ATTRIBUTE #else @@ -183,74 +205,294 @@ } } // 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) { +enum allocationOwnership { NEITHER = 0, ASAN = 1, RTL = 2 }; +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); + +LPVOID SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc, + FreeFunction freeFunc, AllocFunction allocFunc, + HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, + SIZE_T dwBytes) { + CHECK(reallocFunc && heapSizeFunc && freeFunc && allocFunc); + size_t old_usable_size = 0; GET_STACK_TRACE_MALLOC; GET_CURRENT_PC_BP_SP; (void)sp; - // Realloc should never reallocate in place. + allocationOwnership ownershipState; + if (flags()->windows_hook_rtl_allocators) + { + 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); + 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; if (dwFlags & HEAP_ZERO_MEMORY) { 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; } -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 { static void TryToOverrideFunction(const char *fname, uptr new_func) { // Failure here is not fatal. The CRT may not be present, and different CRT // versions use different symbols. if (!__interception::OverrideFunction(fname, new_func)) VPrintf(2, "Failed to override function %s\n", fname); } void ReplaceSystemMalloc() { #if defined(ASAN_DYNAMIC) TryToOverrideFunction("free", (uptr)free); @@ -272,6 +514,34 @@ 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,17 +549,9 @@ // 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 #endif // _WIN32 Index: compiler-rt/lib/asan/asan_win.cc =================================================================== --- compiler-rt/lib/asan/asan_win.cc +++ compiler-rt/lib/asan/asan_win.cc @@ -197,26 +197,55 @@ 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; + +#define 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; } void *AsanTSDGet() { CHECK(tsd_key_inited); +if (flags()->windows_hook_rtl_allocators) + return IsTlsInitialized() ? fake_tsd : nullptr; +else return fake_tsd; } void AsanTSDSet(void *tsd) { CHECK(tsd_key_inited); fake_tsd = tsd; } void PlatformTSDDtor(void *tsd) { AsanThread::TSDDtor(tsd); } // }}} // ---------------------- Various stuff ---------------- {{{ void *AsanDoesNotSupportStaticLinkage() { #if defined(_DEBUG) @@ -256,33 +285,38 @@ 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; } // Only handle access violations that land within the shadow memory. uptr addr = (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. // Determine the address of the page that is being accessed. uptr page = RoundDownTo(addr, page_size); // Commit the page. uptr result = (uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE); if (result != page) return EXCEPTION_CONTINUE_SEARCH; // The page mapping succeeded, so continue execution as usual. return EXCEPTION_CONTINUE_EXECUTION; } #endif void InitializePlatformExceptionHandlers() { #if SANITIZER_WINDOWS64 Index: compiler-rt/test/asan/TestCases/Windows/dll_host.cc =================================================================== --- compiler-rt/test/asan/TestCases/Windows/dll_host.cc +++ compiler-rt/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 Index: compiler-rt/test/asan/TestCases/Windows/dll_unload.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/dll_unload.cc @@ -0,0 +1,52 @@ +#include +#include + +// 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 +#include +#include +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 +} Index: compiler-rt/test/asan/TestCases/Windows/heapalloc.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/heapalloc.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 + +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 +} Index: compiler-rt/test/asan/TestCases/Windows/heapalloc_dll_double_free.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/heapalloc_dll_double_free.cc @@ -0,0 +1,40 @@ +#include +#include + +// 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 +#include +#include +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 +} Index: compiler-rt/test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cc @@ -0,0 +1,40 @@ +#include +#include + +// 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 +#include +#include +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 +} Index: compiler-rt/test/asan/TestCases/Windows/heapalloc_doublefree.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/heapalloc_doublefree.cc @@ -0,0 +1,17 @@ +// 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 +#include + +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 Index: compiler-rt/test/asan/TestCases/Windows/heapalloc_flags_fallback.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/heapalloc_flags_fallback.cc @@ -0,0 +1,19 @@ +// 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 +#include +#include +#include + + 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 +} Index: compiler-rt/test/asan/TestCases/Windows/heapalloc_huge.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/heapalloc_huge.cc @@ -0,0 +1,8 @@ +// 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 +#include +int main(){ + void* nope = HeapAlloc(GetProcessHeap(),0,((size_t)0)-1); + return (nope == nullptr) ? 0 : 1; +} \ No newline at end of file Index: compiler-rt/test/asan/TestCases/Windows/heapalloc_rtl_transfer.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/heapalloc_rtl_transfer.cc @@ -0,0 +1,100 @@ +#include +#include +#include +#include "sanitizer\allocator_interface.h" + +// RUN: %clang_cl_asan %s -o%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// REQUIRES: asan-32-bits +// REQUIRES: asan-rtl-heap-interception + +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 Index: compiler-rt/test/asan/TestCases/Windows/heapalloc_sanity.cc =================================================================== --- /dev/null +++ compiler-rt/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 +#include + +int main() { + char *buffer; + buffer = (char*)HeapAlloc(GetProcessHeap(), 0, 32), + buffer[0] = 'a'; + HeapFree(GetProcessHeap(), 0, buffer); + puts("Okay"); +// CHECK: Okay +} Index: compiler-rt/test/asan/TestCases/Windows/heapalloc_transfer.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/heapalloc_transfer.cc @@ -0,0 +1,28 @@ +#include +#include +#include +#include "sanitizer\allocator_interface.h" +// RUN: %clang_cl_asan %s -o%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// REQUIRES: asan-32-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 Index: compiler-rt/test/asan/TestCases/Windows/heapalloc_uaf.cc =================================================================== --- /dev/null +++ compiler-rt/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 + +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 +} 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,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 + +#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/heaprealloc.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/heaprealloc.cc @@ -0,0 +1,24 @@ +// 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 +#include + +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]] +} Index: compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cc @@ -0,0 +1,57 @@ +// 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 +#include +#include + + +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 Index: compiler-rt/test/asan/TestCases/Windows/heaprealloc_zero_size.cc =================================================================== --- compiler-rt/test/asan/TestCases/Windows/heaprealloc_zero_size.cc +++ compiler-rt/test/asan/TestCases/Windows/heaprealloc_zero_size.cc @@ -1,11 +1,12 @@ // 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 #include #include #include int main() { HANDLE heap = HeapCreate(0, 0, 0); void *ptr = HeapAlloc(heap, 0, 4); Index: compiler-rt/test/asan/TestCases/Windows/queue_user_work_item_report.cc =================================================================== --- compiler-rt/test/asan/TestCases/Windows/queue_user_work_item_report.cc +++ compiler-rt/test/asan/TestCases/Windows/queue_user_work_item_report.cc @@ -10,12 +10,12 @@ 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: 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; } int main(int argc, char **argv) { done = CreateEvent(0, false, false, "job is done"); if (!done) Index: compiler-rt/test/asan/TestCases/Windows/rtlallocateheap.cc =================================================================== --- /dev/null +++ compiler-rt/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 +#include + +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 +} Index: compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_double_free.cc =================================================================== --- /dev/null +++ compiler-rt/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 +#include +#include + +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 +} Index: compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_realloc.cc =================================================================== --- /dev/null +++ compiler-rt/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 +#include +#include + +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 +} Index: compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_flags_fallback.cc =================================================================== --- /dev/null +++ compiler-rt/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 +#include +#include + +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 +} Index: compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_zero.cc =================================================================== --- /dev/null +++ compiler-rt/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 +#include +#include + +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 +}