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/.gitignore =================================================================== --- compiler-rt/.gitignore +++ compiler-rt/.gitignore @@ -4,3 +4,4 @@ multi_arch *.sw? *.pyc +*intercepted_windows_functions.txt \ No newline at end of file Index: compiler-rt/CMakeLists.txt =================================================================== --- compiler-rt/CMakeLists.txt +++ compiler-rt/CMakeLists.txt @@ -414,9 +414,15 @@ list(APPEND SANITIZER_COMMON_CFLAGS -g) endif() +if(MSVC) + if(ASAN_WINDOWS_HOOK_RTL_HEAP) + add_definitions(/DASAN_WINDOWS_HOOK_RTL_HEAP=1) + endif() +endif() + if(LLVM_ENABLE_MODULES) # Sanitizers cannot be built with -fmodules. The interceptors intentionally # don't include system headers, which is incompatible with modules. list(APPEND SANITIZER_COMMON_CFLAGS -fno-modules) endif() 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,20 @@ // Windows-specific malloc interception. //===----------------------------------------------------------------------===// +#ifdef ASAN_WINDOWS_HOOK_RTL_HEAP +#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 +#endif #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_WINDOWS // Intentionally not including windows.h here, to avoid the risk of @@ -24,7 +38,14 @@ #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" @@ -182,68 +203,278 @@ return 0; } } // extern "C" +INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags, + LPCVOID lpMem) { +#ifdef ASAN_WINDOWS_HOOK_RTL_HEAP + if (UNLIKELY(!asan_inited || (!__sanitizer_get_ownership(lpMem) && + HeapValidate(hHeap, 0, lpMem)))) { + return REAL(HeapSize)(hHeap, dwFlags, lpMem); + } +#else + CHECK(dwFlags == 0 && "unsupported heap flags"); +#endif // ASAN_WINDOWS_HOOK_RTL_HEAP + 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) { +#ifdef ASAN_WINDOWS_HOOK_RTL_HEAP + // If the ASAN runtime is not initialized, or we encounter an unsupported + // flag, fall back to the original allocator. + if (UNLIKELY(!asan_inited || + (dwFlags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) { + return REAL(HeapAlloc)(hHeap, dwFlags, dwBytes); + } +#else + CHECK((HEAP_ALLOCATE_UNSUPPORTED_FLAGS & dwFlags) != 0 && + "unsupported flags"); +#endif // ASAN_WINDOWS_HOOK_RTL_HEAP 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"); +#ifdef ASAN_WINDOWS_HOOK_RTL_HEAP + // 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 ((!__sanitizer_get_ownership(lpMem) && HeapValidate(hHeap, 0, lpMem))) { + return REAL(HeapFree)(hHeap, dwFlags, lpMem); + } + +#else + CHECK((HEAP_FREE_UNSUPPORTED_FLAGS & dwFlags) != 0 && "unsupported flags"); +#endif // ASAN_WINDOWS_HOOK_RTL_HEAP 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; +#ifdef ASAN_WINDOWS_HOOK_RTL_HEAP + 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"); +#endif // ASAN_WINDOWS_HOOK_RTL_HEAP + // 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); +} +#ifdef ASAN_WINDOWS_HOOK_RTL_HEAP +// 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 (UNLIKELY(!asan_inited || (!__sanitizer_get_ownership(BaseAddress) && + HeapValidate(HeapHandle, 0, 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 (UNLIKELY((HEAP_FREE_UNSUPPORTED_FLAGS & Flags) != 0 || + (!__sanitizer_get_ownership(BaseAddress) && + HeapValidate(HeapHandle, 0, 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 (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. + return SharedReAlloc(REAL(RtlReAllocateHeap), REAL(RtlSizeHeap), + REAL(RtlFreeHeap), REAL(RtlAllocateHeap), HeapHandle, + Flags, BaseAddress, Size); +} + +#endif // ASAN_WINDOWS_HOOK_RTL_HEAP + 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. @@ -272,6 +503,24 @@ TryToOverrideFunction("_expand", (uptr)_expand); TryToOverrideFunction("_expand_base", (uptr)_expand); +#ifdef ASAN_WINDOWS_HOOK_RTL_HEAP + 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 // 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 @@ -288,8 +537,9 @@ INTERCEPT_UCRT_FUNCTION(HeapReAlloc); INTERCEPT_UCRT_FUNCTION(HeapSize); #undef INTERCEPT_UCRT_FUNCTION -#endif +#endif // ASAN_WINDOWS_HOOK_RTL_HEAP +#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,16 +197,47 @@ static __declspec(thread) void *fake_tsd = 0; +#ifdef ASAN_WINDOWS_HOOK_RTL_HEAP +// 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; +} + +#endif // ASAN_WINDOWS_HOOK_RTL_HEAP + void AsanTSDInit(void (*destructor)(void *tsd)) { // FIXME: we're ignoring the destructor for now. tsd_key_inited = true; } void *AsanTSDGet() { CHECK(tsd_key_inited); +#ifdef ASAN_WINDOWS_HOOK_RTL_HEAP + return IsTlsInitialized() ? fake_tsd : nullptr; +#else return fake_tsd; +#endif // ASAN_WINDOWS_HOOK_RTL_HEAP } void AsanTSDSet(void *tsd) { CHECK(tsd_key_inited); fake_tsd = tsd; @@ -256,19 +287,24 @@ 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); Index: compiler-rt/test/CMakeLists.txt =================================================================== --- compiler-rt/test/CMakeLists.txt +++ compiler-rt/test/CMakeLists.txt @@ -96,3 +96,27 @@ add_custom_target(compiler-rt-test-depends DEPENDS ${LLVM_LIT_DEPENDS}) add_dependencies(check-all check-compiler-rt) endif() + +if(ASAN_WINDOWS_HOOK_RTL_HEAP) +file(WRITE asan/TestCases/Windows/intercepted_windows_functions.txt "__asan_wrap_HeapAlloc +__asan_wrap_HeapFree +__asan_wrap_HeapReAlloc +__asan_wrap_HeapSize +__asan_wrap_CreateThread +__asan_wrap_RaiseException +__asan_wrap_RtlRaiseException +__asan_wrap_SetUnhandledExceptionFilter +__asan_wrap_RtlAllocateHeap +__asan_wrap_RtlFreeHeap +__asan_wrap_RtlReAllocateHeap +__asan_wrap_RtlSizeHeap") +else() +file(WRITE asan/TestCases/Windows/intercepted_windows_functions.txt "__asan_wrap_HeapAlloc +__asan_wrap_HeapFree +__asan_wrap_HeapReAlloc +__asan_wrap_HeapSize +__asan_wrap_CreateThread +__asan_wrap_RaiseException +__asan_wrap_RtlRaiseException +__asan_wrap_SetUnhandledExceptionFilter") +endif() \ No newline at end of file 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 @@ -23,15 +23,7 @@ // RUN: > %t.imports1 // // Add functions interecepted in asan_malloc.win.cc and asan_win.cc. -// RUN: grep '[I]MPORT:' %s | sed -e 's/.*[I]MPORT: //' > %t.imports2 -// IMPORT: __asan_wrap_HeapAlloc -// IMPORT: __asan_wrap_HeapFree -// IMPORT: __asan_wrap_HeapReAlloc -// IMPORT: __asan_wrap_HeapSize -// IMPORT: __asan_wrap_CreateThread -// IMPORT: __asan_wrap_RaiseException -// IMPORT: __asan_wrap_RtlRaiseException -// IMPORT: __asan_wrap_SetUnhandledExceptionFilter +// RUN: cat %S\intercepted_windows_functions.txt > %t.imports2 // // 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,53 @@ +#include +#include + +// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll +// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe +// RUN: 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) + +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,14 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits +// REQUIRES: asan-rtl-heap-interception + +#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,41 @@ +#include +#include + +// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll +// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe +// RUN: 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) + +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,41 @@ +#include +#include + +// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll +// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe +// RUN: 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) + +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,18 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits +// REQUIRES: asan-rtl-heap-interception + +#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,18 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %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,7 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %env_asan_opts=allocator_may_return_null=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: %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,29 @@ +#include +#include +#include +#include "sanitizer\allocator_interface.h" +// RUN: %clang_cl_asan %s -o%t +// RUN: %run %t 2>&1 | FileCheck %s +// REQUIRES: asan-32-bits +// REQUIRES: asan-rtl-heap-interception + +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,14 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits +// REQUIRES: asan-rtl-heap-interception +#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,22 @@ +// RUN: %clang_cl_asan /Od -o %t %s +// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %clang_cl /Od -o %t %s +// RUN: %run %t 2>&1 | FileCheck %s + +#include +#include +#include + +int main() { + HANDLE heap = HeapCreate(0, 0, 0); + void *ptr = HeapAlloc(heap, 0, 4); + assert(ptr); + void *ptr2 = HeapReAlloc(heap, 0, ptr, 0); + assert(ptr2); + HeapFree(heap, 0, ptr2); + fprintf(stderr, "passed!\n"); +} + +// CHECK-NOT: double-free +// CHECK-NOT: AddressSanitizer +// CHECK: passed! Index: compiler-rt/test/asan/TestCases/Windows/heaprealloc.cc =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/Windows/heaprealloc.cc @@ -0,0 +1,25 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits +// REQUIRES: asan-rtl-heap-interception + +#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: %run %t 2>&1 | FileCheck %s +// XFAIL: asan-64-bits +// REQUIRES: asan-rtl-heap-interception +#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/lit.local.cfg =================================================================== --- compiler-rt/test/asan/TestCases/Windows/lit.local.cfg +++ compiler-rt/test/asan/TestCases/Windows/lit.local.cfg @@ -12,3 +12,6 @@ config.unsupported = False else: config.unsupported = True + +if 'RUN_RTL_INTERCEPTION_TESTS' in os.environ: + config.available_features.add('asan-rtl-heap-interception') 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: 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: 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: 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: %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: 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 +}