diff --git a/compiler-rt/lib/asan/asan_flags.inc b/compiler-rt/lib/asan/asan_flags.inc --- a/compiler-rt/lib/asan/asan_flags.inc +++ b/compiler-rt/lib/asan/asan_flags.inc @@ -158,6 +158,3 @@ ASAN_FLAG(bool, verify_asan_link_order, true, "Check position of ASan runtime in library list (needs to be disabled" " when other library has to be preloaded system-wide)") -ASAN_FLAG( - bool, windows_hook_rtl_allocators, false, - "(Windows only) enable hooking of Rtl(Allocate|Free|Size|ReAllocate)Heap.") diff --git a/compiler-rt/lib/asan/asan_malloc_win.cpp b/compiler-rt/lib/asan/asan_malloc_win.cpp --- a/compiler-rt/lib/asan/asan_malloc_win.cpp +++ b/compiler-rt/lib/asan/asan_malloc_win.cpp @@ -11,44 +11,57 @@ // Windows-specific malloc interception. //===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_addrhashmap.h" #include "sanitizer_common/sanitizer_allocator_interface.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_WINDOWS +#include + #include "asan_allocator.h" #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_stack.h" +#include "asan_win_immortalize.h" +#include "asan_win_scoped_lock.h" #include "interception/interception.h" -#include // Intentionally not including windows.h here, to avoid the risk of // pulling in conflicting declarations of these functions. (With mingw-w64, // there's a risk of windows.h pulling in stdint.h.) -typedef int BOOL; -typedef void *HANDLE; +typedef void *HANDLE, *LPVOID, *PHANDLE, *HGLOBAL, *HLOCAL, *HWND; typedef const void *LPCVOID; -typedef void *LPVOID; +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned long DWORD, LOGICAL; + +struct _RTL_HEAP_PARAMETERS; +typedef _RTL_HEAP_PARAMETERS *PRTL_HEAP_PARAMETERS; -typedef unsigned long DWORD; +using __sanitizer::uptr; + +constexpr unsigned long HEAP_NO_SERIALIZE = 0x00000001; +constexpr unsigned long HEAP_GENERATE_EXCEPTIONS = 0x00000004; constexpr unsigned long HEAP_ZERO_MEMORY = 0x00000008; constexpr unsigned long HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010; -constexpr unsigned long HEAP_ALLOCATE_SUPPORTED_FLAGS = (HEAP_ZERO_MEMORY); +constexpr unsigned long HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; + +constexpr unsigned long HEAP_ALLOCATE_SUPPORTED_FLAGS = + (HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY); constexpr unsigned long HEAP_ALLOCATE_UNSUPPORTED_FLAGS = (~HEAP_ALLOCATE_SUPPORTED_FLAGS); -constexpr unsigned long HEAP_FREE_UNSUPPORTED_FLAGS = - (~HEAP_ALLOCATE_SUPPORTED_FLAGS); -constexpr unsigned long HEAP_REALLOC_UNSUPPORTED_FLAGS = - (~HEAP_ALLOCATE_SUPPORTED_FLAGS); +constexpr unsigned long HEAP_REALLOC_SUPPORTED_FLAGS = + (HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY); +constexpr unsigned long HEAP_REALLOC_UNSUPPORTED_FLAGS = + (~HEAP_REALLOC_SUPPORTED_FLAGS); extern "C" { -LPVOID WINAPI HeapAlloc(HANDLE hHeap, DWORD dwFlags, size_t dwBytes); -LPVOID WINAPI HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, - size_t dwBytes); -BOOL WINAPI HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); -size_t WINAPI HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); +HANDLE WINAPI GetProcessHeap(); +BOOL WINAPI HeapValidate(HANDLE, DWORD, void *); -BOOL WINAPI HeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); +DWORD WINAPI GetCurrentThreadId(); } using namespace __asan; @@ -199,282 +212,543 @@ int _CrtSetReportMode(int, int) { return 0; } + } // 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); -} +struct AsanHeapMemoryNode { + static void *operator new(size_t size) { return InternalAlloc(size); } + static void operator delete(void *p) { InternalFree(p); } + + AsanHeapMemoryNode(void *_memory) : memory(_memory) {} + + void *memory; + AsanHeapMemoryNode *next; +}; + +typedef __sanitizer::IntrusiveList AsanMemoryList; +typedef __sanitizer::AddrHashMap AsanMemoryMap; + +struct AsanHeap { + // This data structure is undocumented and is subject to change. + struct HEAP { +#if SANITIZER_WORDSIZE == 64 + unsigned long padding[28]; +#elif SANITIZER_WORDSIZE == 32 + unsigned long padding[16]; +#else +#error "Platform not supported" +#endif + unsigned long flags; + unsigned long forceFlags; + }; + + static void *operator new(size_t size) { return InternalAlloc(size); } + static void *operator new(size_t, void *p) { return p; } + static void operator delete(void *p) { InternalFree(p); } + + explicit AsanHeap(HANDLE _heap) : heap(*((HEAP *)_heap)) { + constexpr unsigned long HEAP_PROCESS_CLASS = 0x00000000; + constexpr unsigned long HEAP_PRIVATE_CLASS = 0x00001000; + constexpr unsigned long HEAP_CLASS_MASK = 0x0000F000; + + constexpr unsigned long HEAP_SUPPORTED_CLASSES[] = {HEAP_PROCESS_CLASS, + HEAP_PRIVATE_CLASS}; + + const unsigned long heapClass = + (heap.flags | heap.forceFlags) & HEAP_CLASS_MASK; + + bool heapClassSupported = false; + for (const auto &heapClassType : HEAP_SUPPORTED_CLASSES) { + if (heapClass == heapClassType) { + heapClassSupported = true; + break; + } + } -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); + if (!heapClassSupported) { + is_supported = false; + return; } - } 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"); + + constexpr unsigned long HEAP_UNSUPPORTED_FLAGS = + (HEAP_GENERATE_EXCEPTIONS | HEAP_REALLOC_IN_PLACE_ONLY | + HEAP_CREATE_ENABLE_EXECUTE); + + if ((heap.flags | heap.forceFlags) & HEAP_UNSUPPORTED_FLAGS) { + is_supported = false; + return; + } + + is_supported = true; } - 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 (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); + + [[nodiscard]] unsigned long GetFlags() const { + constexpr unsigned long HEAP_EXAMINED_FLAGS = + (HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY | HEAP_REALLOC_IN_PLACE_ONLY); + + return (heap.flags | heap.forceFlags) & HEAP_EXAMINED_FLAGS; } - return p; + + // A reference to some members of the opaque HEAP data structure. + const HEAP &heap; + + // A lock to keep the accesses to the map and the list atomic. + __sanitizer::SpinMutex lock = {}; + + // We need to keep track of which thread holds the lock if any. + __sanitizer::atomic_uint32_t thread_id = {}; + + // A list of memory managed by asan associated with this heap to enable + // freeing all memory when a heap is destroyed. + AsanMemoryList asan_memory = {}; + + // A mapping of asan managed pointers to the node before them in the list to + // allow for efficient removal when freed. + AsanMemoryMap memory_map; + + bool is_supported; +}; + +struct AsanHeapMap : public __sanitizer::AddrHashMap { + using __sanitizer::AddrHashMap::Handle; + + static void *operator new(size_t, void *p) { return p; } +}; + +AsanHeapMap *GetAsanHeapMap() { return &immortalize(); } +AsanHeap *GetDefaultHeap() { + return &immortalize(GetProcessHeap()); } -INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) { - // 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); +AsanHeap *GetAsanHeap(void *heap) { + AsanHeap *asan_heap; + if (heap == GetProcessHeap()) { + asan_heap = GetDefaultHeap(); } else { - CHECK((HEAP_FREE_UNSUPPORTED_FLAGS & dwFlags) != 0 && "unsupported flags"); + AsanHeapMap::Handle h_find_or_create( + GetAsanHeapMap(), reinterpret_cast(heap), false, true); + + if (h_find_or_create.created()) { + asan_heap = new AsanHeap(heap); + *h_find_or_create = asan_heap; + } else { + asan_heap = *h_find_or_create; + } } - GET_STACK_TRACE_FREE; - asan_free(lpMem, &stack, FROM_MALLOC); - return true; + + return asan_heap; } -namespace __asan { -using AllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, size_t); -using ReAllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, LPVOID, size_t); -using SizeFunction = size_t(WINAPI *)(HANDLE, DWORD, LPVOID); -using FreeFunction = BOOL(WINAPI *)(HANDLE, DWORD, LPVOID); - -void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc, - FreeFunction freeFunc, AllocFunction allocFunc, - HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, size_t dwBytes) { - CHECK(reallocFunc && heapSizeFunc && freeFunc && allocFunc); - GET_STACK_TRACE_MALLOC; - GET_CURRENT_PC_BP_SP; - (void)sp; - if (flags()->windows_hook_rtl_allocators) { - enum AllocationOwnership { NEITHER = 0, ASAN = 1, RTL = 2 }; - AllocationOwnership ownershipState; - bool owned_rtlalloc = false; - bool owned_asan = __sanitizer_get_ownership(lpMem); - - if (!owned_asan) - owned_rtlalloc = HeapValidate(hHeap, 0, lpMem); - - if (owned_asan && !owned_rtlalloc) - ownershipState = ASAN; - else if (!owned_asan && owned_rtlalloc) - ownershipState = RTL; - else if (!owned_asan && !owned_rtlalloc) - ownershipState = NEITHER; - - // If this heap block which was allocated before the ASAN - // runtime came up, use the real HeapFree function. - if (UNLIKELY(!asan_inited)) { - return reallocFunc(hHeap, dwFlags, lpMem, dwBytes); - } - bool only_asan_supported_flags = - (HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) == 0; - - if (ownershipState == RTL || - (ownershipState == NEITHER && !only_asan_supported_flags)) { - if (only_asan_supported_flags) { - // if this is a conversion to ASAN upported flags, transfer this - // allocation to the ASAN allocator - void *replacement_alloc; - if (dwFlags & HEAP_ZERO_MEMORY) - replacement_alloc = asan_calloc(1, dwBytes, &stack); - else - replacement_alloc = asan_malloc(dwBytes, &stack); - if (replacement_alloc) { - size_t old_size = heapSizeFunc(hHeap, dwFlags, lpMem); - if (old_size == ((size_t)0) - 1) { - asan_free(replacement_alloc, &stack, FROM_MALLOC); - return nullptr; - } - REAL(memcpy)(replacement_alloc, lpMem, old_size); - freeFunc(hHeap, dwFlags, lpMem); - } - return replacement_alloc; - } else { - // owned by rtl or neither with unsupported ASAN flags, - // just pass back to original allocator - CHECK(ownershipState == RTL || ownershipState == NEITHER); - CHECK(!only_asan_supported_flags); - return reallocFunc(hHeap, dwFlags, lpMem, dwBytes); - } - } +struct AllocationOwnership { + enum { NEITHER = 0, ASAN = 1, RTL = 2 }; + const int ownership; - if (ownershipState == ASAN && !only_asan_supported_flags) { - // Conversion to unsupported flags allocation, - // transfer this allocation back to the original allocator. - void *replacement_alloc = allocFunc(hHeap, dwFlags, dwBytes); - size_t old_usable_size = 0; - if (replacement_alloc) { - old_usable_size = asan_malloc_usable_size(lpMem, pc, bp); - REAL(memcpy)(replacement_alloc, lpMem, - Min(dwBytes, old_usable_size)); - asan_free(lpMem, &stack, FROM_MALLOC); - } - return replacement_alloc; + AllocationOwnership(void *heap, void *memory) + : ownership(get_ownership(heap, memory)) {} + + private: + int get_ownership(void *heap, void *memory) { + if (__sanitizer_get_ownership(memory)) { + return ASAN; + } else if (HeapValidate(heap, 0, memory)) { + return RTL; } - 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"); + return NEITHER; } - // 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; - - // 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); + friend bool operator==(const AllocationOwnership &l, + const AllocationOwnership &r) { + return l.ownership == r.ownership; + } - void *ptr = asan_realloc(lpMem, dwBytes, &stack); - if (ptr == nullptr) - return nullptr; + friend bool operator==(const AllocationOwnership &l, const int &r) { + return l.ownership == r; + } - 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); + friend bool operator==(const int &l, const AllocationOwnership &r) { + return l == r.ownership; } - return ptr; -} -} // namespace __asan + template + friend bool operator!=(const AllocationOwnership &l, const Other &r) { + return !(l == r); + } -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); -} + template + friend bool operator!=(const Other &l, const AllocationOwnership &r) { + return !(l == r); + } +}; // 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 unsigned long LOGICAL; // This function is documented as part of the Driver Development Kit but *not* // the Windows Development Kit. -LOGICAL RtlFreeHeap(void* HeapHandle, DWORD Flags, - void* BaseAddress); +void *RtlDestroyHeap(void *HeapHandle); + +// This function is documented as part of the Driver Development Kit but *not* +// the Windows Development Kit. +LOGICAL RtlFreeHeap(void *HeapHandle, DWORD Flags, void *BaseAddress); // This function is documented as part of the Driver Development Kit but *not* // the Windows Development Kit. -void* RtlAllocateHeap(void* HeapHandle, DWORD Flags, size_t Size); +void *RtlAllocateHeap(void *HeapHandle, DWORD Flags, size_t Size); // This function is completely undocumented. -void* -RtlReAllocateHeap(void* HeapHandle, DWORD Flags, void* BaseAddress, - size_t Size); +void *RtlReAllocateHeap(void *HeapHandle, DWORD Flags, void *BaseAddress, + size_t Size); // This function is completely undocumented. -size_t RtlSizeHeap(void* HeapHandle, DWORD Flags, void* BaseAddress); +size_t RtlSizeHeap(void *HeapHandle, DWORD Flags, void *BaseAddress); + +// TODO: This doesn't gracefully handle the situation that one thread is trying +// to use this heap while it is being destroyed. +INTERCEPTOR_WINAPI(void *, RtlDestroyHeap, void *HeapHandle) { + AsanHeapMap::Handle h_delete(GetAsanHeapMap(), + reinterpret_cast(HeapHandle), true, false); + if (UNLIKELY(!h_delete.exists() && HeapHandle != GetProcessHeap())) { + return REAL(RtlDestroyHeap)(HeapHandle); + } + + AsanHeap *asan_heap = GetAsanHeap(HeapHandle); + + GET_STACK_TRACE_FREE; + asan_heap->lock.Lock(); + + // Free all memory managed by asan and associated with this heap. + while (!asan_heap->asan_memory.empty()) { + asan_free(asan_heap->asan_memory.front()->memory, &stack, FROM_MALLOC); + delete asan_heap->asan_memory.front(); + asan_heap->asan_memory.pop_front(); + } + + if (HeapHandle != GetProcessHeap()) { + delete asan_heap; + } + + return REAL(RtlDestroyHeap)(HeapHandle); +} INTERCEPTOR_WINAPI(size_t, RtlSizeHeap, HANDLE HeapHandle, DWORD Flags, - void* BaseAddress) { - if (!flags()->windows_hook_rtl_allocators || - UNLIKELY(!asan_inited || OWNED_BY_RTL(HeapHandle, BaseAddress))) { + void *BaseAddress) { + if (UNLIKELY(!asan_inited || !BaseAddress)) { + return REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress); + } + + AllocationOwnership owner(HeapHandle, BaseAddress); + if (UNLIKELY(owner != AllocationOwnership::ASAN)) { return REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress); } + + // We could check to make sure the BaseAddress belongs to the heap that + // was passed in here but it is not strictly necessary. + GET_CURRENT_PC_BP_SP; (void)sp; return asan_malloc_usable_size(BaseAddress, pc, bp); } -INTERCEPTOR_WINAPI(BOOL, RtlFreeHeap, HANDLE HeapHandle, DWORD Flags, - void* 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); +INTERCEPTOR_WINAPI(void *, RtlAllocateHeap, HANDLE HeapHandle, DWORD Flags, + size_t Size) { + if (UNLIKELY(!asan_inited)) { + return REAL(RtlAllocateHeap)(HeapHandle, Flags, Size); } - GET_STACK_TRACE_FREE; - asan_free(BaseAddress, &stack, FROM_MALLOC); - return true; -} -INTERCEPTOR_WINAPI(void*, 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)) { + AsanHeap *asan_heap = GetAsanHeap(HeapHandle); + if (UNLIKELY(!asan_heap->is_supported)) { + return REAL(RtlAllocateHeap)(HeapHandle, Flags, Size); + } + + const DWORD all_flags = Flags | asan_heap->GetFlags(); + const DWORD unsupported_flags = all_flags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS; + + if (UNLIKELY(unsupported_flags)) { return REAL(RtlAllocateHeap)(HeapHandle, Flags, Size); } + + // Take the lock in the AsanHeap + RecursiveScopedLock raii_lock(asan_heap->lock, asan_heap->thread_id); + GET_STACK_TRACE_MALLOC; - void *p; + void *p = asan_malloc(Size, &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 (Flags & HEAP_ZERO_MEMORY) { - p = asan_calloc(Size, 1, &stack); - } else { - p = asan_malloc(Size, &stack); + if (p && (Flags & 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); + } + + AsanHeapMemoryNode *mem_node = new AsanHeapMemoryNode(p); + AsanHeapMemoryNode *prev_tail = asan_heap->asan_memory.back(); + asan_heap->asan_memory.push_back(mem_node); + + { + AsanMemoryMap::Handle h(&(asan_heap->memory_map), reinterpret_cast(p), + false, true); + *h = prev_tail; } + return p; } -INTERCEPTOR_WINAPI(void*, RtlReAllocateHeap, HANDLE HeapHandle, DWORD Flags, - void* 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) +INTERCEPTOR_WINAPI(LOGICAL, RtlFreeHeap, void *HeapHandle, DWORD Flags, + void *BaseAddress) { + if (UNLIKELY(!asan_inited || !BaseAddress)) { + return REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress); + } + + AllocationOwnership owner(HeapHandle, BaseAddress); + if (UNLIKELY(owner == AllocationOwnership::RTL)) { + return REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress); + } + + if (owner == AllocationOwnership::NEITHER) { + GET_STACK_TRACE_FREE; + // This should either return double-free or wild pointer errors + asan_free(BaseAddress, &stack, FROM_MALLOC); + + return false; + } + + // ASAN owns the memory + AsanHeap *asan_heap = GetAsanHeap(HeapHandle); + + // Take the lock in the AsanHeap + RecursiveScopedLock raii_lock(asan_heap->lock, asan_heap->thread_id); + + AsanHeapMemoryNode *found; + { + AsanMemoryMap::Handle h_delete(&(asan_heap->memory_map), + reinterpret_cast(BaseAddress), true, + false); + + // If the pointer is not in the heap's allocated memory map one of + // two things could be happening: + // 1. The memory passed into RtlFreeHeap was allocated with malloc or new. + // 2. ASAN owns the memory but the wrong heap was passed into RtlFreeHeap + // and we should emulate RtlFreeHeap's behavior. + if (!h_delete.exists()) { + if (HeapHandle == GetProcessHeap()) { + GET_STACK_TRACE_FREE; + asan_free(BaseAddress, &stack, FROM_MALLOC); + + return true; + } else { + return REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress); + } + } + + found = *h_delete; + } + + AsanHeapMemoryNode *remove; + AsanHeapMemoryNode *update; + if (found) { + remove = found->next; + asan_heap->asan_memory.extract(found, found->next); + update = found->next; + } else { + remove = asan_heap->asan_memory.front(); + asan_heap->asan_memory.pop_front(); + update = asan_heap->asan_memory.front(); + } + + CHECK(remove->memory == BaseAddress && + "Memory list is inconsistent with map. " + "This is a bug, please report it."); + + if (update) { + { + AsanMemoryMap::Handle h_update(&(asan_heap->memory_map), + reinterpret_cast(update->memory), + true, false); + } + { + AsanMemoryMap::Handle h_update(&(asan_heap->memory_map), + reinterpret_cast(update->memory), + false, true); + *h_update = found; + } + } + + delete remove; + + GET_STACK_TRACE_FREE; + asan_free(BaseAddress, &stack, FROM_MALLOC); + + return true; +} + +INTERCEPTOR_WINAPI(void *, RtlReAllocateHeap, HANDLE HeapHandle, DWORD Flags, + void *BaseAddress, size_t Size) { + if (UNLIKELY(!asan_inited)) { + return REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size); + } + + if (UNLIKELY(!BaseAddress)) { + return WRAP(RtlAllocateHeap)(HeapHandle, Flags, Size); + } + + AllocationOwnership owner(HeapHandle, BaseAddress); + + AsanHeap *asan_heap = GetAsanHeap(HeapHandle); + if (UNLIKELY(!asan_heap->is_supported)) { return REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size); + } + + const DWORD all_flags = Flags | asan_heap->GetFlags(); + const DWORD asan_unsupported_flags = + (HEAP_REALLOC_UNSUPPORTED_FLAGS & all_flags); + + // Take the lock in the AsanHeap + RecursiveScopedLock raii_lock(asan_heap->lock, asan_heap->thread_id); + + GET_STACK_TRACE_MALLOC; + GET_CURRENT_PC_BP_SP; + (void)sp; + + void *replacement_alloc = nullptr; + size_t old_size; + if (owner == AllocationOwnership::NEITHER) { + // This should cause a use-after-free or wild pointer error. If it is a + // wild pointer error the pointer was either nonsense or came from + // another heap. + replacement_alloc = asan_realloc(BaseAddress, Size, &stack); + CHECK((all_flags & HEAP_ZERO_MEMORY) == 0 && + "We cannot zero the memory as we do not know the previous size of " + "the memory. This error should only occur if ASAN errors are " + "non-fatal."); + + if (replacement_alloc) { + AsanHeapMemoryNode *mem_node = new AsanHeapMemoryNode(replacement_alloc); + AsanHeapMemoryNode *prev_tail = asan_heap->asan_memory.back(); + asan_heap->asan_memory.push_back(mem_node); + + { + AsanMemoryMap::Handle h(&(asan_heap->memory_map), + reinterpret_cast(replacement_alloc), + false, true); + *h = prev_tail; + } + } + } else if (!asan_unsupported_flags && owner == AllocationOwnership::ASAN) { + // 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 (Size == 0) { + Size = 1; + } + + if (all_flags & HEAP_ZERO_MEMORY) { + old_size = asan_malloc_usable_size(BaseAddress, pc, bp); + } - return SharedReAlloc(REAL(RtlReAllocateHeap), REAL(RtlSizeHeap), - REAL(RtlFreeHeap), REAL(RtlAllocateHeap), HeapHandle, - Flags, BaseAddress, Size); + AsanHeapMemoryNode *found; + bool new_malloc_rtl_mismatch = false; + { + AsanMemoryMap::Handle h_delete(&(asan_heap->memory_map), + reinterpret_cast(BaseAddress), true, + false); + + // If the pointer is not in the heap's allocated memory map one of + // two things could be happening: + // 1. The memory passed into RtlReAllocateHeap was allocated with malloc + // or new. + // 2. ASAN owns the memory but the wrong heap was passed into + // RtlReAllocateHeap and we should emulate RtlReAllocateHeap's behavior. + if (!h_delete.exists()) { + if (HeapHandle == GetProcessHeap()) { + new_malloc_rtl_mismatch = true; + } else { + return REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size); + } + } + + found = *h_delete; + } + + replacement_alloc = asan_realloc(BaseAddress, Size, &stack); + if (replacement_alloc == nullptr) { + return nullptr; + } + + if (all_flags & HEAP_ZERO_MEMORY) { + size_t new_size = asan_malloc_usable_size(replacement_alloc, pc, bp); + if (old_size < new_size) { + REAL(memset) + (((u8 *)replacement_alloc) + old_size, 0, new_size - old_size); + } + } + + // We need to remove the old pointer from both the heap list and the heap + // map and then add the new pointer. + if (replacement_alloc != BaseAddress) { + if (!new_malloc_rtl_mismatch) { + if (found) { + found->next->memory = replacement_alloc; + } else { + asan_heap->asan_memory.front()->memory = replacement_alloc; + } + } else { + // If the function which created this allocation was not an Rtl + // function we just add the new memory onto the end of the linked + // list. + AsanHeapMemoryNode *mem_node = + new AsanHeapMemoryNode(replacement_alloc); + found = asan_heap->asan_memory.back(); + asan_heap->asan_memory.push_back(mem_node); + } + + AsanMemoryMap::Handle h_new(&(asan_heap->memory_map), + reinterpret_cast(replacement_alloc), + false, true); + *h_new = found; + } + } else if (UNLIKELY(!asan_unsupported_flags && + owner == AllocationOwnership::RTL)) { + old_size = REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress); + + if (old_size != ~size_t{0}) { + replacement_alloc = WRAP(RtlAllocateHeap)(HeapHandle, Flags, Size); + if (replacement_alloc == nullptr) { + return nullptr; + } else { + REAL(memcpy) + (replacement_alloc, BaseAddress, Min(Size, old_size)); + REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress); + } + } else { + return nullptr; + } + } else if (UNLIKELY(asan_unsupported_flags && + owner == AllocationOwnership::ASAN)) { + // Conversion to unsupported flags allocation, + // transfer this allocation to the original allocator. + replacement_alloc = REAL(RtlAllocateHeap)(HeapHandle, Flags, Size); + + if (replacement_alloc) { + old_size = asan_malloc_usable_size(BaseAddress, pc, bp); + REAL(memcpy)(replacement_alloc, BaseAddress, Min(Size, old_size)); + WRAP(RtlFreeHeap)(HeapHandle, Flags, BaseAddress); + } + } else if (UNLIKELY(asan_unsupported_flags && + owner == AllocationOwnership::RTL)) { + // Currently owned by rtl using unsupported ASAN flags, + // just pass back to original allocator. + replacement_alloc = + REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size); + } + + return replacement_alloc; } namespace __asan { @@ -507,43 +781,19 @@ 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 - // ucrtbase.dll so that all attempts to use the lower-level win32 heap - // 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. - + // 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)); + __interception::OverrideFunction("RtlDestroyHeap", (uptr)WRAP(RtlDestroyHeap), + (uptr *)&REAL(RtlDestroyHeap)); #endif // defined(ASAN_DYNAMIC) } } // namespace __asan diff --git a/compiler-rt/lib/asan/asan_win_dynamic_runtime_thunk.cpp b/compiler-rt/lib/asan/asan_win_dynamic_runtime_thunk.cpp --- a/compiler-rt/lib/asan/asan_win_dynamic_runtime_thunk.cpp +++ b/compiler-rt/lib/asan/asan_win_dynamic_runtime_thunk.cpp @@ -22,10 +22,11 @@ #ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK #define SANITIZER_IMPORT_INTERFACE 1 -#include "sanitizer_common/sanitizer_win_defs.h" #define WIN32_LEAN_AND_MEAN #include +#include "sanitizer_common/sanitizer_win_defs.h" + // Define weak alias for all weak functions imported from asan dll. #define INTERFACE_FUNCTION(Name) #define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name) diff --git a/compiler-rt/lib/asan/asan_win_immortalize.h b/compiler-rt/lib/asan/asan_win_immortalize.h new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_immortalize.h @@ -0,0 +1,58 @@ +//===-- asan_win_immortalize.h --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Windows-specific thread-safe and pre-CRT global initialization safe +// infrastructure to create an object whose destructor is never called. +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_win_defs.h" +// These types are required to satisfy XFG which requires that the names of the +// types for indirect calls to be correct as well as the name of the original +// type for any typedefs. +typedef union _RTL_RUN_ONCE* PINIT_ONCE; +typedef void* PVOID; +typedef int BOOL; + +extern "C" { +__declspec(dllimport) int WINAPI + InitOnceExecuteOnce(void**, BOOL(WINAPI*)(PINIT_ONCE, PVOID, PVOID*), void*, + void*); +} + +template +BOOL WINAPI immortalize_impl(PINIT_ONCE, PVOID storage_ptr, PVOID*) noexcept { + // Ty must provide a placement new operator + new (storage_ptr) Ty(); + return 1; +} + +template +BOOL WINAPI immortalize_impl(PINIT_ONCE, PVOID storage_ptr, + PVOID* param) noexcept { + // Ty must provide a placement new operator + new (storage_ptr) Ty(*((Arg*)param)); + return 1; +} + +template +Ty& immortalize() { // return a reference to an object that will live forever + static void* flag; + alignas(Ty) static unsigned char storage[sizeof(Ty)]; + InitOnceExecuteOnce(&flag, immortalize_impl, &storage, nullptr); + return reinterpret_cast(storage); +} + +template +Ty& immortalize( + Arg arg) { // return a reference to an object that will live forever + static void* flag; + alignas(Ty) static unsigned char storage[sizeof(Ty)]; + InitOnceExecuteOnce(&flag, immortalize_impl, &storage, &arg); + return reinterpret_cast(storage); +} diff --git a/compiler-rt/lib/asan/asan_win_scoped_lock.h b/compiler-rt/lib/asan/asan_win_scoped_lock.h new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/asan/asan_win_scoped_lock.h @@ -0,0 +1,43 @@ +//===-- asan_win_scoped_lock.h --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// raii lock used exclusively by Windows-specific parts of ASan +//===----------------------------------------------------------------------===// +#pragma once + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_mutex.h" + +extern "C" unsigned long _stdcall GetCurrentThreadId(); + +class RecursiveScopedLock { + public: + explicit RecursiveScopedLock(__sanitizer::SpinMutex &_lock, + __sanitizer::atomic_uint32_t &_thread_id) + : lock(_lock), thread_id(_thread_id), serialized(false) { + if (atomic_load_relaxed(&thread_id) != GetCurrentThreadId()) { + lock.Lock(); + atomic_store_relaxed(&thread_id, GetCurrentThreadId()); + serialized = true; + } + } + + ~RecursiveScopedLock() { + if (serialized) { + atomic_store_relaxed(&thread_id, 0); + lock.Unlock(); + } + } + + private: + __sanitizer::SpinMutex &lock; + __sanitizer::atomic_uint32_t &thread_id; + bool serialized; +}; diff --git a/compiler-rt/lib/interception/interception_win.cpp b/compiler-rt/lib/interception/interception_win.cpp --- a/compiler-rt/lib/interception/interception_win.cpp +++ b/compiler-rt/lib/interception/interception_win.cpp @@ -423,6 +423,11 @@ case 0x6A: // 6A XX = push XX return 2; + // This instruction can be encoded with a 16-bit immediate but that is + // incredibly unlikely. + case 0x68: // 68 XX XX XX XX : push imm32 + return 5; + case 0xb8: // b8 XX XX XX XX : mov eax, XX XX XX XX case 0xB9: // b9 XX XX XX XX : mov ecx, XX XX XX XX return 5; @@ -458,8 +463,13 @@ case 0xc889: // 89 C8 : mov eax, ecx case 0xC18B: // 8B C1 : mov eax, ecx case 0xC033: // 33 C0 : xor eax, eax + case 0x8bec: // EC 8B : mov ebp, esp case 0xC933: // 33 C9 : xor ecx, ecx case 0xD233: // 33 D2 : xor edx, edx + case 0xc084: // 84 c0 : test al,al + case 0xdb84: // 84 db : test bl,bl + case 0xc984: // 84 c9 : test cl,cl + case 0xd284: // 84 d2 : test dl,dl return 2; // Cannot overwrite control-instruction. Return 0 to indicate failure. @@ -468,6 +478,9 @@ } switch (0x00FFFFFF & *(u32*)address) { + case 0x83e4f8: // F8 E4 83 : and esp, 0xFFFFFFF8 + case 0x83ec64: // 64 EC 83 : sub esp, 64h + return 3; case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX] return 7; } @@ -477,6 +490,20 @@ case 0xA1: // A1 XX XX XX XX XX XX XX XX : // movabs eax, dword ptr ds:[XXXXXXXX] return 9; + case 0xf2: + switch (*(u32 *)(address + 1)) { + case 0x2444110f: // f2 0f 11 44 24 XX movsd mmword ptr [rsp + XX], + // xmm0 + case 0x244c110f: // f2 0f 11 4c 24 XX movsd QWORD PTR + // [rsp+0x8],xmm1 + case 0x2454110f: // f2 0f 11 54 24 XX movsd QWORD PTR + // [rsp+0x8],xmm2 + case 0x245c110f: // f2 0f 11 5c 24 XX movsd QWORD PTR + // [rsp+0x8],xmm3 + case 0x2464110f: // f2 0f 11 64 24 XX movsd QWORD PTR + // [rsp+0x8],xmm4 + return 6; + } } switch (*(u16*)address) { @@ -495,10 +522,26 @@ case 0x9066: // Two-byte NOP return 2; + case 0x7e80: // 80 7e YY XX cmp BYTE PTR [rsi+YY], XX + case 0x7d80: // 80 7d YY XX cmp BYTE PTR [rdx+YY], XX + case 0x7a80: // 80 7a YY XX cmp BYTE PTR [rdx+YY], XX + case 0x7880: // 80 78 YY XX cmp BYTE PTR [rax+YY], XX + case 0x7b80: // 80 7b YY XX cmp BYTE PTR [rbx+YY], XX + case 0x7980: // 80 79 YY XX cmp BYTE ptr [rcx+YY], XX + return 4; + case 0x058B: // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX] if (rel_offset) *rel_offset = 2; return 6; + + case 0x7e81: // 81 7e YY XX XX XX XX cmp DWORD PTR [rsi+YY], XX XX XX XX + case 0x7d81: // 81 7d YY XX XX XX XX cmp DWORD PTR [rdx+YY], XX XX XX XX + case 0x7a81: // 81 7a YY XX XX XX XX cmp DWORD PTR [rdx+YY], XX XX XX XX + case 0x7881: // 81 78 YY XX XX XX XX cmp DWORD PTR [rax+YY], XX XX XX XX + case 0x7b81: // 81 78 YY XX XX XX XX cmp DWORD PTR [rbx+YY], XX XX XX XX + case 0x7981: // 81 79 YY XX XX XX XX cmp dword ptr [rcx+YY], XX XX XX XX + return 7; } switch (0x00FFFFFF & *(u32*)address) { @@ -510,6 +553,17 @@ case 0x07c1f6: // f6 c1 07 : test cl, 0x7 case 0xc98548: // 48 85 C9 : test rcx, rcx case 0xc0854d: // 4d 85 c0 : test r8, r8 + case 0xdb8548: // 48 85 db : test rbx,rbx + case 0xd28548: // 48 85 d2 : test rdx,rdx + case 0xc9854d: // 4d 85 c9 : test r9,r9 + case 0xd2854d: // 4d 85 d2 : test r10,r10 + case 0xdb854d: // 4d 85 db : test r11,r11 + case 0xe4854d: // 4d 85 e4 : test r12,r12 + case 0xed854d: // 4d 85 ed : test r13,r13 + case 0xf6854d: // 4d 85 f6 : test r14,r14 + case 0xff854d: // 4d 85 ff : test r15,r15 + case 0xed8548: // 48 85 ed : test rbp,rbp + case 0xe48548: // 48 85 e4 : test rsp,rsp case 0xc2b60f: // 0f b6 c2 : movzx eax, dl case 0xc03345: // 45 33 c0 : xor r8d, r8d case 0xc93345: // 45 33 c9 : xor r9d, r9d @@ -526,16 +580,60 @@ case 0xdc8b4c: // 4c 8b dc : mov r11, rsp case 0xd18b4c: // 4c 8b d1 : mov r10, rcx case 0xE0E483: // 83 E4 E0 : and esp, 0xFFFFFFE0 + case 0xc1ff48: // 48 ff c1 : inc rcx + case 0xc0ff48: // 48 ff c0 : inc rax + case 0xc3ff48: // 48 ff c3 : inc rbx + case 0xc2ff48: // 48 ff c2 : inc rdx + case 0xc0ff49: // 49 ff c0 : inc r8 + case 0xc1ff49: // 49 ff c1 : inc r9 + case 0xc2ff49: // 49 ff c2 : inc r10 + case 0xc3ff49: // 49 ff c3 : inc r11 + case 0xc4ff49: // 49 ff c4 : inc r12 + case 0xc5ff49: // 49 ff c5 : inc r13 + case 0xc6ff49: // 49 ff c6 : inc r14 + case 0xc7ff49: // 49 ff c7 : inc r15 + case 0xc6ff48: // 48 ff c6 : inc rsi + case 0xc7ff48: // 48 ff c7 : inc rdi + case 0xc08b41: // 41 8b c0 : mov eax,r8d + case 0xc18b41: // 41 8b c1 : mov eax,r9d + case 0xc28b41: // 41 8b c2 : mov eax,r10d + case 0xc38b41: // 41 8b c3 : mov eax,r11d + case 0xc48b41: // 41 8b c4 : mov eax,r12d + return 3; case 0xec8348: // 48 83 ec XX : sub rsp, XX case 0xf88349: // 49 83 f8 XX : cmp r8, XX case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx + case 0x245489: // 89 54 24 XX : mov DWORD PTR[rsp + XX], edx return 4; + case 0x246483: // 83 64 24 00 00 : and DWORD PTR [rsp+xx],0x0 + return 5; + + case 0x7e8166: // 66 81 7e YY XX XX cmp WORD PTR [rsi+0xYY], XX XX + case 0x7f8166: // 66 81 7f YY XX XX cmp WORD PTR [rdi+0xYY], XX XX + case 0x788166: // 66 81 78 YY XX XX cmp WORD PTR [rax+0xYY], XX XX + case 0x7b8166: // 66 81 7b YY XX XX cmp WORD PTR [rbx+0xYY], XX XX + case 0x798166: // 66 81 79 YY XX XX cmp WORD PTR [rcx+0xYY], XX XX + case 0x7a8166: // 66 81 7a YY XX XX cmp WORD PTR [rdx+0xYY], XX XX + return 6; + case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX return 7; + case 0x788141: // 41 81 78 YY XX XX XX XX cmp DWORD PTR [r8+YY], XX XX XX + // XX + case 0x798141: // r9 + case 0x7a8141: // r10 + case 0x7b8141: // r11 + case 0x7c8141: // r12 + case 0x7d8141: // r13 + case 0x7e8141: // r14 + case 0x7f8141: // 41 81 78 YY XX XX XX XX cmp DWORD P [r15+YY], XX XX XX XX + case 0x247c81: // 81 7c 24 YY XX XX XX XX cmp DWORD P [rsp+YY], XX XX XX XX + return 8; + case 0x058b48: // 48 8b 05 XX XX XX XX : // mov rax, QWORD PTR [rip + XXXXXXXX] case 0x25ff48: // 48 ff 25 XX XX XX XX : @@ -560,6 +658,10 @@ case 0x24548948: // 48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx case 0x244c894c: // 4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9 case 0x2444894c: // 4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8 + case 0x244c8944: // 44 89 4c 24 XX mov DWORD PTR [rsp + XX], r9d + case 0x24448944: // 44 89 44 24 XX mov DWORD PTR [rsp + XX], r8d + case 0x247c8948: // 48 89 7c 24 XX mov QWORD ptr [rsp + XX], rdi + case 0x246c8d48: // 48 8d 6c 24 XX : lea rbp, [rsp + XX] return 5; case 0x24648348: // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY return 6; diff --git a/compiler-rt/test/asan/TestCases/Windows/dll_host.cpp b/compiler-rt/test/asan/TestCases/Windows/dll_host.cpp --- a/compiler-rt/test/asan/TestCases/Windows/dll_host.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/dll_host.cpp @@ -24,16 +24,13 @@ // // Add functions interecepted in asan_malloc.win.cpp and asan_win.cpp. // 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 // IMPORT: __asan_wrap_RtlSizeHeap // IMPORT: __asan_wrap_RtlAllocateHeap +// IMPORT: __asan_wrap_RtlDestroyHeap // IMPORT: __asan_wrap_RtlReAllocateHeap // IMPORT: __asan_wrap_RtlFreeHeap // diff --git a/compiler-rt/test/asan/TestCases/Windows/dll_unload.cpp b/compiler-rt/test/asan/TestCases/Windows/dll_unload.cpp --- a/compiler-rt/test/asan/TestCases/Windows/dll_unload.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/dll_unload.cpp @@ -3,7 +3,7 @@ // 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 +// RUN: not %run %te.exe %t.dll 2>&1 | FileCheck %s // REQUIRES: asan-dynamic-runtime // REQUIRES: asan-32-bits diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc.cpp b/compiler-rt/test/asan/TestCases/Windows/heapalloc.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heapalloc.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc.cpp @@ -1,6 +1,6 @@ // UNSUPPORTED: asan-64-bits // RUN: %clang_cl_asan -Od %s -Fe%t -// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// RUN: not %run %t 2>&1 | FileCheck %s #include diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_dll_double_free.cpp b/compiler-rt/test/asan/TestCases/Windows/heapalloc_dll_double_free.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heapalloc_dll_double_free.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc_dll_double_free.cpp @@ -3,7 +3,7 @@ // 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 +// RUN: not %run %te.exe %t.dll 2>&1 | FileCheck %s // REQUIRES: asan-dynamic-runtime // REQUIRES: asan-32-bits diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cpp b/compiler-rt/test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cpp @@ -3,7 +3,7 @@ // 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 +// RUN: not %run %te.exe %t.dll 2>&1 | FileCheck %s // REQUIRES: asan-dynamic-runtime // REQUIRES: asan-32-bits diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_doublefree.cpp b/compiler-rt/test/asan/TestCases/Windows/heapalloc_doublefree.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heapalloc_doublefree.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc_doublefree.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cl_asan -Od %s -Fe%t -// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// RUN: not %run %t 2>&1 | FileCheck %s // UNSUPPORTED: asan-64-bits #include #include diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_flags_fallback.cpp b/compiler-rt/test/asan/TestCases/Windows/heapalloc_flags_fallback.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heapalloc_flags_fallback.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc_flags_fallback.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cl_asan -Od %s -Fe%t // RUN: %run %t 2>&1 | FileCheck %s -// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// RUN: %run %t 2>&1 | FileCheck %s // UNSUPPORTED: asan-64-bits #include #include diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_huge.cpp b/compiler-rt/test/asan/TestCases/Windows/heapalloc_huge.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heapalloc_huge.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc_huge.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cl_asan -Od %s -Fe%t // RUN: %env_asan_opts=allocator_may_return_null=true %run %t -// RUN: %env_asan_opts=allocator_may_return_null=true:windows_hook_rtl_allocators=true %run %t +// RUN: %env_asan_opts=allocator_may_return_null=true %run %t // UNSUPPORTED: asan-64-bits #include int main() { diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_rtl_transfer.cpp b/compiler-rt/test/asan/TestCases/Windows/heapalloc_rtl_transfer.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heapalloc_rtl_transfer.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc_rtl_transfer.cpp @@ -4,7 +4,7 @@ #include // RUN: %clang_cl_asan %s -o%t -// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// RUN: %run %t 2>&1 | FileCheck %s // UNSUPPORTED: asan-64-bits using AllocateFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, SIZE_T); diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_transfer.cpp b/compiler-rt/test/asan/TestCases/Windows/heapalloc_transfer.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heapalloc_transfer.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc_transfer.cpp @@ -3,7 +3,7 @@ #include #include // RUN: %clang_cl_asan %s -o%t -// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// RUN: %run %t 2>&1 | FileCheck %s // UNSUPPORTED: asan-64-bits int main() { diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_uaf.cpp b/compiler-rt/test/asan/TestCases/Windows/heapalloc_uaf.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heapalloc_uaf.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc_uaf.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cl_asan -Od %s -Fe%t -// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// RUN: not %run %t 2>&1 | FileCheck %s // UNSUPPORTED: asan-64-bits #include diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_zero_size.cpp b/compiler-rt/test/asan/TestCases/Windows/heapalloc_zero_size.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heapalloc_zero_size.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc_zero_size.cpp @@ -1,6 +1,6 @@ // 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: %run %t 2>&1 | FileCheck %s +// RUN: %run %t 2>&1 | FileCheck %s // RUN: %clang_cl /Od -o %t %s // RUN: %run %t 2>&1 | FileCheck %s // UNSUPPORTED: asan-64-bits diff --git a/compiler-rt/test/asan/TestCases/Windows/heapcreate.cpp b/compiler-rt/test/asan/TestCases/Windows/heapcreate.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/heapcreate.cpp @@ -0,0 +1,20 @@ +#include "sanitizer\allocator_interface.h" +#include +#include + +// RUN: %clang_cl_asan -Od %s -Fe%t +// RUN: %run %t 2>&1 | FileCheck %s + +int main() { + void *newHeap = HeapCreate(0, 1000, 5000); + void *newAlloc = HeapAlloc(newHeap, 0, 100); + if (__sanitizer_get_ownership(newAlloc)) { + printf("success\n"); + return 0; + } + printf("fail\n"); + return 1; +} + +// CHECK: success +// CHECK-NOT: fail diff --git a/compiler-rt/test/asan/TestCases/Windows/heapcreate_double_free.cpp b/compiler-rt/test/asan/TestCases/Windows/heapcreate_double_free.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/heapcreate_double_free.cpp @@ -0,0 +1,19 @@ +#include "sanitizer\allocator_interface.h" +#include +#include + +// RUN: %clang_cl_asan -Od %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +int main() { + void *newHeap = HeapCreate(0, 0, 0); + void *newAlloc = HeapAlloc(newHeap, 0, 100); + + HeapFree(newHeap, 0, newAlloc); + HeapFree(newHeap, 0, newAlloc); + printf("failure\n"); + return 1; +} + +// CHECK: AddressSanitizer: double-free +// CHECK-NOT: failure; \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/heapcreate_executable.cpp b/compiler-rt/test/asan/TestCases/Windows/heapcreate_executable.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/heapcreate_executable.cpp @@ -0,0 +1,20 @@ +#include "sanitizer\allocator_interface.h" +#include +#include + +// RUN: %clang_cl_asan -Od %s -Fe%t +// RUN: %run %t 2>&1 | FileCheck %s + +int main() { + void *newHeap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0); + void *newAlloc = HeapAlloc(newHeap, 0, 100); + if (!__sanitizer_get_ownership(newAlloc)) { + printf("success\n"); + return 0; + } + printf("fail\n"); + return 1; +} + +// CHECK: success +// CHECK-NOT: fail diff --git a/compiler-rt/test/asan/TestCases/Windows/heapcreate_free_after_destroy.cpp b/compiler-rt/test/asan/TestCases/Windows/heapcreate_free_after_destroy.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/heapcreate_free_after_destroy.cpp @@ -0,0 +1,21 @@ +#include "sanitizer\allocator_interface.h" +#include +#include + +// RUN: %clang_cl_asan -Od %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +int main() { + void *newHeap = HeapCreate(0, 0, 0); + void *newAlloc = HeapAlloc(newHeap, 0, 100); + HeapDestroy(newHeap); + + HeapFree(newHeap, 0, newAlloc); + printf("failure\n"); + return 1; +} + +// We need to add an address sanitizer error for using a heap after it has +// been destroyed. For now we get an access-violation error. +// CHECK: AddressSanitizer: +// CHECK-NOT: failure; diff --git a/compiler-rt/test/asan/TestCases/Windows/heapcreate_sanity.cpp b/compiler-rt/test/asan/TestCases/Windows/heapcreate_sanity.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/heapcreate_sanity.cpp @@ -0,0 +1,25 @@ +#include "sanitizer\allocator_interface.h" +#include +#include + +// RUN: %clang_cl_asan -Od %s -Fe%t +// RUN: %run %t 2>&1 | FileCheck %s + +int main() { + void *newHeap = HeapCreate(0, 1000, 5000); // make a new heap with accepted flags for the asan interceptors + char *newAlloc = (char *)HeapAlloc(newHeap, 0, 100); // Allocate, this should belong to the sanitizer runtime + // check that we've created a new heap and that the allocation belongs where we think it should + if (newHeap != GetProcessHeap() && __sanitizer_get_ownership(newAlloc)) { + //touch the allocations and make sure nothing unexpected happens. + newAlloc[0] = 0xff; + newAlloc[99] = 0xff; + printf("success\n"); + return 0; + } + printf("fail\n"); + return 1; +} + +// CHECK: success +// CHECK-NOT: fail +// CHECK-NOT: AddressSanitizer diff --git a/compiler-rt/test/asan/TestCases/Windows/heapdestroy_report.cpp b/compiler-rt/test/asan/TestCases/Windows/heapdestroy_report.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/heapdestroy_report.cpp @@ -0,0 +1,17 @@ +#include "sanitizer\allocator_interface.h" +#include +#include + +// RUN: %clang_cl_asan -Od %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +int main() { + void *newHeap = HeapCreate(0, 0, 0); + char *newAlloc = (char *)HeapAlloc(newHeap, 0, 100); + HeapDestroy(newHeap); + newAlloc[0] = 0xff; + return 1; +} + +// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 diff --git a/compiler-rt/test/asan/TestCases/Windows/heapdestroy_sanity.cpp b/compiler-rt/test/asan/TestCases/Windows/heapdestroy_sanity.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/heapdestroy_sanity.cpp @@ -0,0 +1,17 @@ +#include "sanitizer\allocator_interface.h" +#include +#include + +// RUN: %clang_cl_asan -Od %s -Fe%t +// RUN: %run %t 2>&1 | FileCheck %s + +int main() { + void *newHeap = HeapCreate(0, 0, 0); + void *newAlloc = HeapAlloc(newHeap, 0, 100); + HeapDestroy(newHeap); + printf("success\n"); + return 0; +} + +// CHECK-NOT: AddressSanitizer +// CHECK: success diff --git a/compiler-rt/test/asan/TestCases/Windows/heaprealloc.cpp b/compiler-rt/test/asan/TestCases/Windows/heaprealloc.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heaprealloc.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heaprealloc.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cl_asan -Od %s -Fe%t -// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// RUN: not %run %t 2>&1 | FileCheck %s // UNSUPPORTED: asan-64-bits #include #include diff --git a/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp b/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cl_asan /Od /MT -o %t %s -// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// RUN: %run %t 2>&1 | FileCheck %s // UNSUPPORTED: asan-64-bits #include #include diff --git a/compiler-rt/test/asan/TestCases/Windows/heaprealloc_zero_size.cpp b/compiler-rt/test/asan/TestCases/Windows/heaprealloc_zero_size.cpp --- a/compiler-rt/test/asan/TestCases/Windows/heaprealloc_zero_size.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/heaprealloc_zero_size.cpp @@ -1,6 +1,6 @@ // 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: %run %t 2>&1 | FileCheck %s +// RUN: %run %t 2>&1 | FileCheck %s // RUN: %clang_cl /Od -o %t %s // RUN: %run %t 2>&1 | FileCheck %s // UNSUPPORTED: asan-64-bits @@ -20,4 +20,4 @@ // CHECK-NOT: double-free // CHECK-NOT: AddressSanitizer -// CHECK: passed! \ No newline at end of file +// CHECK: passed! diff --git a/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap.cpp b/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap.cpp --- a/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cl_asan -Od %s -Fe%t /MD -// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// RUN: not %run %t 2>&1 | FileCheck %s // UNSUPPORTED: asan-64-bits // REQUIRES: asan-rtl-heap-interception diff --git a/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_double_free.cpp b/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_double_free.cpp --- a/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_double_free.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_double_free.cpp @@ -1,6 +1,6 @@ // 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 +// RUN: not %run %te.exe %t.dll 2>&1 | FileCheck %s // REQUIRES: asan-dynamic-runtime // REQUIRES: asan-32-bits // REQUIRES: asan-rtl-heap-interception diff --git a/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_realloc.cpp b/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_realloc.cpp --- a/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_realloc.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_realloc.cpp @@ -1,6 +1,6 @@ // 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 +// RUN: not %run %te.exe %t.dll 2>&1 | FileCheck %s // REQUIRES: asan-dynamic-runtime // REQUIRES: asan-32-bits // REQUIRES: asan-rtl-heap-interception diff --git a/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_flags_fallback.cpp b/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_flags_fallback.cpp --- a/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_flags_fallback.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_flags_fallback.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cl_asan -Od %s -Fe%t /MD -// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// RUN: %run %t 2>&1 | FileCheck %s // UNSUPPORTED: asan-64-bits // REQUIRES: asan-rtl-heap-interception diff --git a/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_zero.cpp b/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_zero.cpp --- a/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_zero.cpp +++ b/compiler-rt/test/asan/TestCases/Windows/rtlallocateheap_zero.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cl_asan -Od %s -Fe%t /MD -// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// RUN: not %run %t 2>&1 | FileCheck %s // UNSUPPORTED: asan-64-bits // REQUIRES: asan-rtl-heap-interception diff --git a/compiler-rt/test/asan/TestCases/alloca_detect_custom_size_.cpp b/compiler-rt/test/asan/TestCases/alloca_detect_custom_size_.cpp --- a/compiler-rt/test/asan/TestCases/alloca_detect_custom_size_.cpp +++ b/compiler-rt/test/asan/TestCases/alloca_detect_custom_size_.cpp @@ -2,19 +2,25 @@ // RUN: not %run %t 2>&1 | FileCheck %s // +#include "defines.h" #include - +#include struct A { char a[3]; int b[3]; }; -__attribute__((noinline)) void foo(int index, int len) { - volatile struct A str[len] __attribute__((aligned(32))); - assert(!(reinterpret_cast(str) & 31L)); +ATTRIBUTE_NOINLINE void foo(int index, int len) { + ATTRIBUTE_ALIGNED(32) +#ifdef MSVC + volatile struct A *str = (volatile struct A *)_alloca(len * sizeof(struct A)); +#else + volatile struct A str[len]; +#endif + assert(!(reinterpret_cast(str) & 31L)); str[index].a[0] = '1'; // BOOM -// CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] -// CHECK: WRITE of size 1 at [[ADDR]] thread T0 + // CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] + // CHECK: WRITE of size 1 at [[ADDR]] thread T0 } int main(int argc, char **argv) { diff --git a/compiler-rt/test/asan/TestCases/alloca_loop_unpoisoning.cpp b/compiler-rt/test/asan/TestCases/alloca_loop_unpoisoning.cpp --- a/compiler-rt/test/asan/TestCases/alloca_loop_unpoisoning.cpp +++ b/compiler-rt/test/asan/TestCases/alloca_loop_unpoisoning.cpp @@ -5,10 +5,11 @@ // This testcase checks that allocas and VLAs inside loop are correctly unpoisoned. +#include "defines.h" +#include "sanitizer/asan_interface.h" #include #include #include -#include "sanitizer/asan_interface.h" // MSVC provides _alloca instead of alloca. #if defined(_MSC_VER) && !defined(alloca) @@ -21,14 +22,25 @@ void *top, *bot; -__attribute__((noinline)) void foo(int len) { +ATTRIBUTE_NOINLINE void foo(int len) { char x; top = &x; + + ATTRIBUTE_ALIGNED(32) +#ifdef MSVC + char *array = (char *)alloca(len); +#else char array[len]; +#endif assert(!(reinterpret_cast(array) & 31L)); alloca(len); for (int i = 0; i < 32; ++i) { - char array[i]; + ATTRIBUTE_ALIGNED(32) +#ifdef MSVC + char *array = (char *)alloca(i); +#else + char array[i]; // NOLINT +#endif bot = alloca(i); assert(!(reinterpret_cast(bot) & 31L)); } diff --git a/compiler-rt/test/asan/TestCases/defines.h b/compiler-rt/test/asan/TestCases/defines.h new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/defines.h @@ -0,0 +1,39 @@ +#pragma once + +#if defined(MSVC) +#include + +#define ATTRIBUTE_NOINLINE __declspec(noinline) +#define ATTRIBUTE_ALIGNED(x) __declspec(align(x)) +#define ATTRIBUTE_NO_SANITIZE_ADDRESS __declspec(no_sanitize_address) +#define ATTRIBUTE_USED /* FIXME: Is there a __declspec used? */ +#define ATTRIBUTE_ALWAYS_INLINE __forceinline +#define VOLATILE volatile +#define EXTRACT_RETURN_ADDRESS _ReturnAddress() +#define ASM_CAUSE_SIDE_EFFECT(dest) __asm { mov eax, dest} +#define MULTIPLE_ATTRIBUTE_DECL(a, b) __declspec(a b) +#ifdef _DEBUG +// _DEBUG tests are compiled with NDEBUG to turn off CRT debug assertions +// that will throw before we can get to the asan report. +#undef assert +#define assert(x) \ + if (!(x)) \ + return 1; +#endif // _DEBUG +#else + +#define ATTRIBUTE_NOINLINE __attribute__((noinline)) +#define ATTRIBUTE_ALIGNED(x) __attribute__((aligned(x))) +#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#define ATTRIBUTE_USED __attribute__((used)) +#define ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) +#define INLINE_ASM(x) __asm__(x) +#define VOLATILE __volatile__ +#define EXTRACT_RETURN_ADDRESS __builtin_extract_return_addr(__builtin_return_address(0)) +#define ASM_CAUSE_SIDE_EFFECT(dest) __asm__ __volatile__("" \ + : \ + : "r"(dest) \ + : "memory"); +#define MULTIPLE_ATTRIBUTE_DECL(a, b) __attribute__((a, b)) + +#endif // _MSC_VER