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 @@ -28,6 +28,10 @@ typedef void *HANDLE; typedef const void *LPCVOID; typedef void *LPVOID; +typedef HANDLE HWND; +typedef HANDLE HGLOBAL; +typedef HANDLE HLOCAL; +typedef unsigned int UINT; typedef unsigned long DWORD; constexpr unsigned long HEAP_ZERO_MEMORY = 0x00000008; @@ -40,7 +44,6 @@ constexpr unsigned long HEAP_REALLOC_UNSUPPORTED_FLAGS = (~HEAP_ALLOCATE_SUPPORTED_FLAGS); - extern "C" { LPVOID WINAPI HeapAlloc(HANDLE hHeap, DWORD dwFlags, size_t dwBytes); LPVOID WINAPI HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, @@ -49,6 +52,22 @@ size_t WINAPI HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); BOOL WINAPI HeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); +HANDLE WINAPI GetProcessHeap(); + +_declspec(dllimport) HGLOBAL WINAPI GlobalAlloc(UINT uFlags, SIZE_T dwBytes); +_declspec(dllimport) HGLOBAL WINAPI GlobalFree(HGLOBAL hMem); +_declspec(dllimport) HGLOBAL WINAPI GlobalSize(HGLOBAL hMem); +_declspec(dllimport) HGLOBAL WINAPI + GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes, UINT uFlags); +_declspec(dllimport) HGLOBAL WINAPI GlobalLock(HGLOBAL hMem); +_declspec(dllimport) HGLOBAL WINAPI GlobalUnlock(HGLOBAL hMem); +_declspec(dllimport) HLOCAL WINAPI LocalAlloc(UINT uFlags, SIZE_T dwBytes); +_declspec(dllimport) HLOCAL WINAPI LocalFree(HLOCAL hMem); +_declspec(dllimport) HLOCAL WINAPI LocalSize(HLOCAL hMem); +_declspec(dllimport) HLOCAL WINAPI + LocalReAlloc(HLOCAL hMem, size_t dwBytes, UINT uFlags); +_declspec(dllimport) HLOCAL WINAPI LocalLock(HLOCAL hMem); +_declspec(dllimport) HLOCAL WINAPI LocalUnlock(HLOCAL hMem); } using namespace __asan; @@ -59,9 +78,9 @@ // so we have to intercept them before they are called for the first time. #if ASAN_DYNAMIC -# define ALLOCATION_FUNCTION_ATTRIBUTE +#define ALLOCATION_FUNCTION_ATTRIBUTE #else -# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +#define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE #endif extern "C" { @@ -73,9 +92,7 @@ } ALLOCATION_FUNCTION_ATTRIBUTE -size_t _msize_base(void *ptr) { - return _msize(ptr); -} +size_t _msize_base(void *ptr) { return _msize(ptr); } ALLOCATION_FUNCTION_ATTRIBUTE void free(void *ptr) { @@ -84,14 +101,10 @@ } ALLOCATION_FUNCTION_ATTRIBUTE -void _free_dbg(void *ptr, int) { - free(ptr); -} +void _free_dbg(void *ptr, int) { free(ptr); } ALLOCATION_FUNCTION_ATTRIBUTE -void _free_base(void *ptr) { - free(ptr); -} +void _free_base(void *ptr) { free(ptr); } ALLOCATION_FUNCTION_ATTRIBUTE void *malloc(size_t size) { @@ -100,14 +113,10 @@ } ALLOCATION_FUNCTION_ATTRIBUTE -void *_malloc_base(size_t size) { - return malloc(size); -} +void *_malloc_base(size_t size) { return malloc(size); } ALLOCATION_FUNCTION_ATTRIBUTE -void *_malloc_dbg(size_t size, int, const char *, int) { - return malloc(size); -} +void *_malloc_dbg(size_t size, int, const char *, int) { return malloc(size); } ALLOCATION_FUNCTION_ATTRIBUTE void *calloc(size_t nmemb, size_t size) { @@ -116,9 +125,7 @@ } ALLOCATION_FUNCTION_ATTRIBUTE -void *_calloc_base(size_t nmemb, size_t size) { - return calloc(nmemb, size); -} +void *_calloc_base(size_t nmemb, size_t size) { return calloc(nmemb, size); } ALLOCATION_FUNCTION_ATTRIBUTE void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) { @@ -143,9 +150,7 @@ } ALLOCATION_FUNCTION_ATTRIBUTE -void *_realloc_base(void *ptr, size_t size) { - return realloc(ptr, size); -} +void *_realloc_base(void *ptr, size_t size) { return realloc(ptr, size); } ALLOCATION_FUNCTION_ATTRIBUTE void *_recalloc(void *p, size_t n, size_t elem_size) { @@ -186,19 +191,16 @@ // TODO(timurrrr): Might want to add support for _aligned_* allocation // functions to detect a bit more bugs. Those functions seem to wrap malloc(). -int _CrtDbgReport(int, const char*, int, - const char*, const char*, ...) { +int _CrtDbgReport(int, const char *, int, const char *, const char *, ...) { ShowStatsAndAbort(); } -int _CrtDbgReportW(int reportType, const wchar_t*, int, - const wchar_t*, const wchar_t*, ...) { +int _CrtDbgReportW(int reportType, const wchar_t *, int, const wchar_t *, + const wchar_t *, ...) { ShowStatsAndAbort(); } -int _CrtSetReportMode(int, int) { - return 0; -} +int _CrtSetReportMode(int, int) { return 0; } } // extern "C" #define OWNED_BY_RTL(heap, memory) \ @@ -339,8 +341,8 @@ 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)); + REAL(memcpy) + (replacement_alloc, lpMem, Min(dwBytes, old_usable_size)); asan_free(lpMem, &stack, FROM_MALLOC); } return replacement_alloc; @@ -402,23 +404,21 @@ // 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); +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, +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); INTERCEPTOR_WINAPI(size_t, RtlSizeHeap, HANDLE HeapHandle, DWORD Flags, - void* BaseAddress) { + void *BaseAddress) { if (!flags()->windows_hook_rtl_allocators || UNLIKELY(!asan_inited || OWNED_BY_RTL(HeapHandle, BaseAddress))) { return REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress); @@ -429,7 +429,7 @@ } INTERCEPTOR_WINAPI(BOOL, RtlFreeHeap, HANDLE HeapHandle, DWORD Flags, - void* BaseAddress) { + 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. @@ -443,7 +443,7 @@ return true; } -INTERCEPTOR_WINAPI(void*, RtlAllocateHeap, HANDLE HeapHandle, DWORD Flags, +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. @@ -465,8 +465,8 @@ return p; } -INTERCEPTOR_WINAPI(void*, RtlReAllocateHeap, HANDLE HeapHandle, DWORD Flags, - void* BaseAddress, size_t Size) { +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) @@ -477,6 +477,287 @@ Flags, BaseAddress, Size); } +// FIXED and ZEROINIT correspond to LMEM_FIXED/GMEM_FIXED +// and LMEM_ZEROINIT/GMEM_ZEROINIT (as provided in the documentation). +// In case, if these values change then FIXED and ZEROINIT +// will have to be updated accordingly. +#define FIXED 0x0000 +#define ZEROINIT 0x0040 + +constexpr unsigned long SHARED_ALLOC_SUPPORTED_FLAGS = (FIXED | ZEROINIT); +constexpr unsigned long SHARED_ALLOC_UNSUPPORTED_FLAGS = + (~SHARED_ALLOC_SUPPORTED_FLAGS); + +INTERCEPTOR_WINAPI(HGLOBAL, GlobalAlloc, UINT uFlags, SIZE_T dwBytes) { + // If we encounter an unsupported flag, then we fall + // back to the original allocator. + if (uFlags & SHARED_ALLOC_UNSUPPORTED_FLAGS) { + return REAL(GlobalAlloc)(uFlags, dwBytes); + } + + GET_STACK_TRACE_MALLOC; + if (uFlags & ZEROINIT) + return asan_calloc(dwBytes, 1, &stack); + else + return asan_malloc(dwBytes, &stack); +} + +INTERCEPTOR_WINAPI(HGLOBAL, GlobalFree, HGLOBAL hMem) { + // If the memory we are trying to free is not owned + // by ASan heap, then fall back to the original GlobalFree. + if (OWNED_BY_RTL(GetProcessHeap(), hMem)) { + return REAL(GlobalFree)(hMem); + } + GET_STACK_TRACE_FREE; + asan_free(hMem, &stack, FROM_MALLOC); + return nullptr; +} + +INTERCEPTOR_WINAPI(SIZE_T, GlobalSize, HGLOBAL hMem) { + // We need to check whether the ASAN allocator owns the pointer + // we're about to use. Allocations might occur before interception + // takes place, so if it is not owned by RTL heap, the we can + // pass it to ASAN heap for inspection. + if (!asan_inited || OWNED_BY_RTL(GetProcessHeap(), hMem)) + return REAL(GlobalSize)(hMem); + + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(hMem, pc, bp); +} + +namespace __asan { +using GlobalLocalAlloc = HANDLE(WINAPI *)(UINT, SIZE_T); +using GlobalLocalRealloc = HANDLE(WINAPI *)(HANDLE, SIZE_T, UINT); +using GlobalLocalSize = SIZE_T(WINAPI *)(HANDLE); +using GlobalLocalFree = HANDLE(WINAPI *)(HANDLE); +using GlobalLocalLock = LPVOID(WINAPI *)(HANDLE); +using GlobalLocalUnlock = LPVOID(WINAPI *)(HANDLE); + +enum class AllocationOwnership { + OWNED_BY_UNKNOWN, + OWNED_BY_ASAN, + OWNED_BY_RTL, + OWNED_BY_GLOBAL_OR_LOCAL, + OWNED_BY_GLOBAL_OR_LOCAL_HANDLE, +}; + +void *RtlToAsan(void *mPtr, size_t old_size, size_t dwBytes, + GlobalLocalFree freeFunc, BufferedStackTrace *stack) { + // Transfer from RTL owned allocation to ASAN owned allocation + void *replacement_alloc; + replacement_alloc = asan_calloc(dwBytes, 1, stack); + if (replacement_alloc) { + if (old_size == ((SIZE_T)0) - 1) { + asan_free(replacement_alloc, stack, FROM_MALLOC); + return nullptr; + } + REAL(memcpy)(replacement_alloc, mPtr, old_size); + freeFunc((HANDLE)mPtr); + } + return replacement_alloc; +} + +void *AsanToRtl(void *mPtr, UINT uFlags, SIZE_T dwBytes, + GlobalLocalAlloc allocFunc, GlobalLocalLock lockFunc, + GlobalLocalLock unlockFunc, BufferedStackTrace *stack, uptr pc, + uptr bp) { + // Transfer from ASAN owned allocation to RTL owned allocation + void *replacement_alloc; + HANDLE mem = allocFunc(uFlags, dwBytes); + // GlobalLock/LocalLock return a pointer to the memory owned by the mem + // handle. We need the pointer to copy the data over from the ASAN owned + // memory. + replacement_alloc = (void *)lockFunc(mem); + size_t old_usable_size = 0; + if (replacement_alloc) { + old_usable_size = asan_malloc_usable_size(mPtr, pc, bp); + REAL(memcpy) + (replacement_alloc, mPtr, Min(dwBytes, old_usable_size)); + asan_free(mPtr, stack, FROM_MALLOC); + } + unlockFunc(mem); + return replacement_alloc; +} + +void *ReAllocToAsan(UINT uFlags, void *mPtr, size_t dwBytes, uptr pc, uptr bp, + BufferedStackTrace *stack) { + // GlobalAlloc, LocalAlloc, GlocalReAlloc and LocalReAlloc all + // accept 0 sized allocations. Passing a zero size into asan_realloc will + // free the allocation. To avoid this and keep behavior consistent, fudge + // the size if zero (asan_malloc already does this). + if (dwBytes == 0) + dwBytes = 1; + + size_t old_size; + if (uFlags & ZEROINIT) + old_size = asan_malloc_usable_size(mPtr, pc, bp); + + void *ptr = asan_realloc(mPtr, dwBytes, stack); + if (ptr == nullptr) + return nullptr; + + if (uFlags & ZEROINIT) { + size_t new_size = asan_malloc_usable_size(ptr, pc, bp); + if (old_size < new_size) + REAL(memset)(((u8 *)ptr) + old_size, 0, new_size - old_size); + } + return ptr; +} + +AllocationOwnership CheckGlobalLocalHeapOwnership( + HANDLE hMem, GlobalLocalLock lockFunc, GlobalLocalUnlock unlockFunc) { + /* To figure the validity of hMem, we use GlobalLock/LocalLock. Those two + * functions can return three things: (1) the pointer that's passed in, in + * which case it is a pointer owned by the Global/Local heap (2) the pointer + * to the allocated object if it's a Global/Local heap HANDLE (3) nullptr if + * it's a pointer which does not belong to the Global/Local heap Using these + * three return types, we figure out if the pointer is TYPE_VALID_PTR or + * TYPE_HANDLE or TYPE_UNKNOWN_PTR + * + * NOTE: As an implementation detail, movable memory objects also live on the + * heap. HeapValidate will return true if given a moveable memory handle. + * + */ + + // Do this first to avoid expensive checks if the pointer is owned by ASAN. + if (__sanitizer_get_ownership(hMem)) { + return AllocationOwnership::OWNED_BY_ASAN; + } + + // It is not safe to pass wild pointers to GlobalLock/LocalLock. + if (HeapValidate(GetProcessHeap(), 0, hMem)) { + void *ptr = lockFunc(hMem); + // We don't care whether ptr is moved after this point as we're just trying + // to determine where it came from. + unlockFunc(hMem); + if (ptr == hMem) { + return AllocationOwnership::OWNED_BY_GLOBAL_OR_LOCAL; + } else if (ptr != nullptr) { + return AllocationOwnership::OWNED_BY_GLOBAL_OR_LOCAL_HANDLE; + } + } + return AllocationOwnership::OWNED_BY_UNKNOWN; +} + +void *ReAllocGlobalLocal(GlobalLocalRealloc reallocFunc, + GlobalLocalSize sizeFunc, GlobalLocalFree freeFunc, + GlobalLocalAlloc allocFunc, GlobalLocalLock lockFunc, + GlobalLocalUnlock unlockFunc, HANDLE hMem, + DWORD dwBytes, UINT uFlags) { + CHECK(reallocFunc && sizeFunc && freeFunc && allocFunc); + GET_STACK_TRACE_MALLOC; + GET_CURRENT_PC_BP_SP; + (void)sp; + + bool only_asan_supported_flags = + (SHARED_ALLOC_UNSUPPORTED_FLAGS & uFlags) == 0; + + AllocationOwnership ownershipState = + CheckGlobalLocalHeapOwnership(hMem, lockFunc, unlockFunc); + + // If this global block which was allocated before the ASAN + // runtime came up OR if mPtr is invalid, use the real GlobalReAlloc function. + if (UNLIKELY(!asan_inited) || + ownershipState == AllocationOwnership::OWNED_BY_GLOBAL_OR_LOCAL_HANDLE) { + return reallocFunc(hMem, dwBytes, uFlags); + } + + // Since hMem is not a handle to moveable memory we may safely cast it to + // pointer. + void *mPtr = (void *)hMem; + + if (ownershipState == AllocationOwnership::OWNED_BY_GLOBAL_OR_LOCAL || + (ownershipState == AllocationOwnership::OWNED_BY_UNKNOWN && + !only_asan_supported_flags)) { + if (only_asan_supported_flags) { + // if this is a conversion to ASAN supported flags, transfer this + // allocation to the ASAN allocator + return RtlToAsan(mPtr, sizeFunc(mPtr), dwBytes, freeFunc, &stack); + } else { + // owned by this heap or neither with unsupported ASAN flags, + // just pass back to original allocator + CHECK(ownershipState == AllocationOwnership::OWNED_BY_GLOBAL_OR_LOCAL || + ownershipState == AllocationOwnership::OWNED_BY_UNKNOWN); + CHECK(!only_asan_supported_flags); + return reallocFunc(mPtr, dwBytes, uFlags); + } + } + + if (ownershipState == AllocationOwnership::OWNED_BY_ASAN && + !only_asan_supported_flags) { + // Conversion to unsupported flags allocation, + // transfer this allocation back to the original allocator. + return AsanToRtl(mPtr, uFlags, dwBytes, allocFunc, lockFunc, unlockFunc, + &stack, pc, bp); + } + + CHECK((ownershipState == AllocationOwnership::OWNED_BY_ASAN || + ownershipState == AllocationOwnership::OWNED_BY_RTL || + ownershipState == AllocationOwnership::OWNED_BY_UNKNOWN) && + only_asan_supported_flags); + + return ReAllocToAsan(uFlags, mPtr, dwBytes, pc, bp, &stack); +} +} // namespace __asan + +INTERCEPTOR_WINAPI(HGLOBAL, GlobalReAlloc, HGLOBAL hMem, DWORD dwBytes, + UINT uFlags) { + return ReAllocGlobalLocal( + (GlobalLocalRealloc)REAL(GlobalReAlloc), + (GlobalLocalSize)REAL(GlobalSize), (GlobalLocalFree)REAL(GlobalFree), + (GlobalLocalAlloc)REAL(GlobalAlloc), (GlobalLocalLock)GlobalLock, + (GlobalLocalUnlock)GlobalUnlock, (HANDLE)hMem, dwBytes, uFlags); +} + +INTERCEPTOR_WINAPI(HLOCAL, LocalAlloc, UINT uFlags, SIZE_T uBytes) { + // If we encounter an unsupported flag, then we fall + // back to the original allocator. + if (uFlags & SHARED_ALLOC_UNSUPPORTED_FLAGS) { + return REAL(LocalAlloc)(uFlags, uBytes); + } + + GET_STACK_TRACE_MALLOC; + if (uFlags & ZEROINIT) + return asan_calloc(uBytes, 1, &stack); + else + return asan_malloc(uBytes, &stack); +} + +INTERCEPTOR_WINAPI(HLOCAL, LocalFree, HGLOBAL hMem) { + // If the memory we are trying to free is not owned + // ASan heap, then fall back to the original LocalFree. + if (OWNED_BY_RTL(GetProcessHeap(), hMem)) { + return REAL(LocalFree)(hMem); + } + + GET_STACK_TRACE_FREE; + asan_free(hMem, &stack, FROM_MALLOC); + return nullptr; +} + +INTERCEPTOR_WINAPI(SIZE_T, LocalSize, HGLOBAL hMem) { + // We need to check whether the ASAN allocator owns the pointer + // we're about to use. Allocations might occur before interception + // takes place, so if it is not owned by RTL heap, the we can + // pass it to ASAN heap for inspection. + if (!asan_inited || OWNED_BY_RTL(GetProcessHeap(), hMem)) + return REAL(LocalSize)(hMem); + + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(hMem, pc, bp); +} + +INTERCEPTOR_WINAPI(HLOCAL, LocalReAlloc, HGLOBAL hMem, DWORD dwBytes, + UINT uFlags) { + return ReAllocGlobalLocal( + (GlobalLocalRealloc)REAL(LocalReAlloc), (GlobalLocalSize)REAL(LocalSize), + (GlobalLocalFree)REAL(LocalFree), (GlobalLocalAlloc)REAL(LocalAlloc), + (GlobalLocalLock)LocalLock, (GlobalLocalUnlock)LocalUnlock, (HANDLE)hMem, + dwBytes, uFlags); +} + namespace __asan { static void TryToOverrideFunction(const char *fname, uptr new_func) { @@ -508,6 +789,14 @@ TryToOverrideFunction("_expand_base", (uptr)_expand); if (flags()->windows_hook_rtl_allocators) { + INTERCEPT_FUNCTION(GlobalAlloc); + INTERCEPT_FUNCTION(GlobalFree); + INTERCEPT_FUNCTION(GlobalSize); + INTERCEPT_FUNCTION(GlobalReAlloc); + INTERCEPT_FUNCTION(LocalAlloc); + INTERCEPT_FUNCTION(LocalFree); + INTERCEPT_FUNCTION(LocalSize); + INTERCEPT_FUNCTION(LocalReAlloc); INTERCEPT_FUNCTION(HeapSize); INTERCEPT_FUNCTION(HeapFree); INTERCEPT_FUNCTION(HeapReAlloc); diff --git a/compiler-rt/lib/interception/interception_win.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 @@ -173,12 +173,12 @@ // ASan sources into interception/. static size_t _strlen(const char *str) { - const char* p = str; + const char *p = str; while (*p != '\0') ++p; return p - str; } -static char* _strchr(char* str, char c) { +static char *_strchr(char *str, char c) { while (*str) { if (*str == c) return str; @@ -188,47 +188,41 @@ } static void _memset(void *p, int value, size_t sz) { - for (size_t i = 0; i < sz; ++i) - ((char*)p)[i] = (char)value; + for (size_t i = 0; i < sz; ++i) ((char *)p)[i] = (char)value; } static void _memcpy(void *dst, void *src, size_t sz) { - char *dst_c = (char*)dst, - *src_c = (char*)src; - for (size_t i = 0; i < sz; ++i) - dst_c[i] = src_c[i]; + char *dst_c = (char *)dst, *src_c = (char *)src; + for (size_t i = 0; i < sz; ++i) dst_c[i] = src_c[i]; } -static bool ChangeMemoryProtection( - uptr address, uptr size, DWORD *old_protection) { - return ::VirtualProtect((void*)address, size, - PAGE_EXECUTE_READWRITE, +static bool ChangeMemoryProtection(uptr address, uptr size, + DWORD *old_protection) { + return ::VirtualProtect((void *)address, size, PAGE_EXECUTE_READWRITE, old_protection) != FALSE; } -static bool RestoreMemoryProtection( - uptr address, uptr size, DWORD old_protection) { +static bool RestoreMemoryProtection(uptr address, uptr size, + DWORD old_protection) { DWORD unused; - return ::VirtualProtect((void*)address, size, - old_protection, - &unused) != FALSE; + return ::VirtualProtect((void *)address, size, old_protection, &unused) != + FALSE; } static bool IsMemoryPadding(uptr address, uptr size) { - u8* function = (u8*)address; + u8 *function = (u8 *)address; for (size_t i = 0; i < size; ++i) if (function[i] != 0x90 && function[i] != 0xCC) return false; return true; } -static const u8 kHintNop8Bytes[] = { - 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 -}; +static const u8 kHintNop8Bytes[] = {0x0F, 0x1F, 0x84, 0x00, + 0x00, 0x00, 0x00, 0x00}; -template +template static bool FunctionHasPrefix(uptr address, const T &pattern) { - u8* function = (u8*)address - sizeof(pattern); + u8 *function = (u8 *)address - sizeof(pattern); for (size_t i = 0; i < sizeof(pattern); ++i) if (function[i] != pattern[i]) return false; @@ -245,23 +239,23 @@ } static void WritePadding(uptr from, uptr size) { - _memset((void*)from, 0xCC, (size_t)size); + _memset((void *)from, 0xCC, (size_t)size); } static void WriteJumpInstruction(uptr from, uptr target) { if (!DistanceIsWithin2Gig(from + kJumpInstructionLength, target)) InterceptionFailed(); ptrdiff_t offset = target - from - kJumpInstructionLength; - *(u8*)from = 0xE9; - *(u32*)(from + 1) = offset; + *(u8 *)from = 0xE9; + *(u32 *)(from + 1) = offset; } static void WriteShortJumpInstruction(uptr from, uptr target) { sptr offset = target - from - kShortJumpInstructionLength; if (offset < -128 || offset > 127) InterceptionFailed(); - *(u8*)from = 0xEB; - *(u8*)(from + 1) = (u8)offset; + *(u8 *)from = 0xEB; + *(u8 *)(from + 1) = (u8)offset; } #if SANITIZER_WINDOWS64 @@ -276,16 +270,15 @@ indirect_target)) { InterceptionFailed(); } - *(u16*)from = 0x25FF; - *(u32*)(from + 2) = offset; + *(u16 *)from = 0x25FF; + *(u32 *)(from + 2) = offset; } #endif -static void WriteBranch( - uptr from, uptr indirect_target, uptr target) { +static void WriteBranch(uptr from, uptr indirect_target, uptr target) { #if SANITIZER_WINDOWS64 WriteIndirectJumpInstruction(from, indirect_target); - *(u64*)indirect_target = target; + *(u64 *)indirect_target = target; #else (void)indirect_target; WriteJumpInstruction(from, target); @@ -319,15 +312,14 @@ uptr scanned = 0; while (scanned < kTrampolineScanLimitRange) { MEMORY_BASIC_INFORMATION info; - if (!::VirtualQuery((void*)address, &info, sizeof(info))) + if (!::VirtualQuery((void *)address, &info, sizeof(info))) return nullptr; // Check whether a region can be allocated at |address|. if (info.State == MEM_FREE && info.RegionSize >= granularity) { - void *page = ::VirtualAlloc((void*)RoundUpTo(address, granularity), - granularity, - MEM_RESERVE | MEM_COMMIT, - PAGE_EXECUTE_READWRITE); + void *page = + ::VirtualAlloc((void *)RoundUpTo(address, granularity), granularity, + MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); return page; } @@ -337,9 +329,7 @@ } return nullptr; #else - return ::VirtualAlloc(nullptr, - granularity, - MEM_RESERVE | MEM_COMMIT, + return ::VirtualAlloc(nullptr, granularity, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); #endif } @@ -350,7 +340,7 @@ TrampolineMemoryRegion *current = &TrampolineRegions[bucket]; if (current->content == 0) return; - ::VirtualFree((void*)current->content, 0, MEM_RELEASE); + ::VirtualFree((void *)current->content, 0, MEM_RELEASE); current->content = 0; } } @@ -359,7 +349,7 @@ // Find a region within 2G with enough space to allocate |size| bytes. TrampolineMemoryRegion *region = nullptr; for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) { - TrampolineMemoryRegion* current = &TrampolineRegions[bucket]; + TrampolineMemoryRegion *current = &TrampolineRegions[bucket]; if (current->content == 0) { // No valid region found, allocate a new region. size_t bucket_size = GetMmapGranularity(); @@ -399,13 +389,13 @@ } // Returns 0 on error. -static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { - switch (*(u64*)address) { +static size_t GetInstructionSize(uptr address, size_t *rel_offset = nullptr) { + switch (*(u64 *)address) { case 0x90909090909006EB: // stub: jmp over 6 x nop. return 8; } - switch (*(u8*)address) { + switch (*(u8 *)address) { case 0x90: // 90 : nop return 1; @@ -451,7 +441,7 @@ return 0; } - switch (*(u16*)(address)) { + switch (*(u16 *)(address)) { case 0x018A: // 8A 01 : mov al, byte ptr [ecx] case 0xFF8B: // 8B FF : mov edi, edi case 0xEC8B: // 8B EC : mov ebp, esp @@ -467,19 +457,19 @@ return 0; } - switch (0x00FFFFFF & *(u32*)address) { + switch (0x00FFFFFF & *(u32 *)address) { case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX] return 7; } #if SANITIZER_WINDOWS64 - switch (*(u8*)address) { + switch (*(u8 *)address) { case 0xA1: // A1 XX XX XX XX XX XX XX XX : // movabs eax, dword ptr ds:[XXXXXXXX] return 9; } - switch (*(u16*)address) { + switch (*(u16 *)address) { case 0x5040: // push rax case 0x5140: // push rcx case 0x5240: // push rdx @@ -501,7 +491,7 @@ return 6; } - switch (0x00FFFFFF & *(u32*)address) { + switch (0x00FFFFFF & *(u32 *)address) { case 0xe58948: // 48 8b c4 : mov rbp, rsp case 0xc18b48: // 48 8b c1 : mov rax, rcx case 0xc48b48: // 48 8b c4 : mov rax, rsp @@ -531,6 +521,7 @@ case 0xec8348: // 48 83 ec XX : sub rsp, XX case 0xf88349: // 49 83 f8 XX : cmp r8, XX case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx + case 0x245489: // 89 54 24 XX : mov DWORD PTR[rsp + XX], edx return 4; case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX @@ -551,7 +542,7 @@ return 8; } - switch (*(u32*)(address)) { + switch (*(u32 *)(address)) { case 0x24448b48: // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX] case 0x246c8948: // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp case 0x245c8948: // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx @@ -567,11 +558,11 @@ #else - switch (*(u8*)address) { + switch (*(u8 *)address) { case 0xA1: // A1 XX XX XX XX : mov eax, dword ptr ds:[XXXXXXXX] return 5; } - switch (*(u16*)address) { + switch (*(u16 *)address) { case 0x458B: // 8B 45 XX : mov eax, dword ptr [ebp + XX] case 0x5D8B: // 8B 5D XX : mov ebx, dword ptr [ebp + XX] case 0x7D8B: // 8B 7D XX : mov edi, dword ptr [ebp + XX] @@ -587,7 +578,7 @@ return 4; } - switch (0x00FFFFFF & *(u32*)address) { + switch (0x00FFFFFF & *(u32 *)address) { case 0x24448A: // 8A 44 24 XX : mov eal, dword ptr [esp + XX] case 0x24448B: // 8B 44 24 XX : mov eax, dword ptr [esp + XX] case 0x244C8B: // 8B 4C 24 XX : mov ecx, dword ptr [esp + XX] @@ -597,7 +588,7 @@ return 4; } - switch (*(u32*)address) { + switch (*(u32 *)address) { case 0x2444B60F: // 0F B6 44 24 XX : movzx eax, byte ptr [esp + XX] return 5; } @@ -629,26 +620,25 @@ while (cursor != size) { size_t rel_offset = 0; size_t instruction_size = GetInstructionSize(from + cursor, &rel_offset); - _memcpy((void*)(to + cursor), (void*)(from + cursor), + _memcpy((void *)(to + cursor), (void *)(from + cursor), (size_t)instruction_size); if (rel_offset) { uptr delta = to - from; - uptr relocated_offset = *(u32*)(to + cursor + rel_offset) - delta; + uptr relocated_offset = *(u32 *)(to + cursor + rel_offset) - delta; #if SANITIZER_WINDOWS64 if (relocated_offset + 0x80000000U >= 0xFFFFFFFFU) return false; #endif - *(u32*)(to + cursor + rel_offset) = relocated_offset; + *(u32 *)(to + cursor + rel_offset) = relocated_offset; } cursor += instruction_size; } return true; } - #if !SANITIZER_WINDOWS64 -bool OverrideFunctionWithDetour( - uptr old_func, uptr new_func, uptr *orig_old_func) { +bool OverrideFunctionWithDetour(uptr old_func, uptr new_func, + uptr *orig_old_func) { const int kDetourHeaderLen = 5; const u16 kDetourInstruction = 0xFF8B; @@ -656,7 +646,7 @@ uptr patch_length = kDetourHeaderLen + kShortJumpInstructionLength; // Validate that the function is hookable. - if (*(u16*)old_func != kDetourInstruction || + if (*(u16 *)old_func != kDetourInstruction || !IsMemoryPadding(header, kDetourHeaderLen)) return false; @@ -682,14 +672,14 @@ } #endif -bool OverrideFunctionWithRedirectJump( - uptr old_func, uptr new_func, uptr *orig_old_func) { +bool OverrideFunctionWithRedirectJump(uptr old_func, uptr new_func, + uptr *orig_old_func) { // Check whether the first instruction is a relative jump. - if (*(u8*)old_func != 0xE9) + if (*(u8 *)old_func != 0xE9) return false; if (orig_old_func) { - uptr relative_offset = *(u32*)(old_func + 1); + uptr relative_offset = *(u32 *)(old_func + 1); uptr absolute_target = old_func + relative_offset + kJumpInstructionLength; *orig_old_func = absolute_target; } @@ -717,8 +707,8 @@ return true; } -bool OverrideFunctionWithHotPatch( - uptr old_func, uptr new_func, uptr *orig_old_func) { +bool OverrideFunctionWithHotPatch(uptr old_func, uptr new_func, + uptr *orig_old_func) { const int kHotPatchHeaderLen = kBranchLength; uptr header = (uptr)old_func - kHotPatchHeaderLen; @@ -767,9 +757,8 @@ return true; } -bool OverrideFunctionWithTrampoline( - uptr old_func, uptr new_func, uptr *orig_old_func) { - +bool OverrideFunctionWithTrampoline(uptr old_func, uptr new_func, + uptr *orig_old_func) { size_t instructions_length = kBranchLength; size_t padding_length = 0; uptr indirect_address = 0; @@ -823,8 +812,7 @@ return true; } -bool OverrideFunction( - uptr old_func, uptr new_func, uptr *orig_old_func) { +bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) { #if !SANITIZER_WINDOWS64 if (OverrideFunctionWithDetour(old_func, new_func, orig_old_func)) return true; @@ -846,10 +834,11 @@ "msvcr120.dll", // VS2013 "vcruntime140.dll", // VS2015 "ucrtbase.dll", // Universal CRT + "KERNELBASE.dll", // KernelBase for GlobalAlloc and LocalAlloc (dynamic) // NTDLL should go last as it exports some functions that we should // override in the CRT [presumably only used internally]. "ntdll.dll", NULL}; - static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 }; + static void *result[ARRAY_SIZE(InterestingDLLs)] = {0}; if (!result[0]) { for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) { if (HMODULE h = GetModuleHandleA(InterestingDLLs[i])) @@ -861,7 +850,8 @@ namespace { // Utility for reading loaded PE images. -template class RVAPtr { +template +class RVAPtr { public: RVAPtr(void *module, uptr rva) : ptr_(reinterpret_cast(reinterpret_cast(module) + rva)) {} @@ -920,12 +910,12 @@ _memcpy(function_name, func, funtion_name_length); function_name[funtion_name_length] = '\0'; - char* separator = _strchr(function_name, '.'); + char *separator = _strchr(function_name, '.'); if (!separator) InterceptionFailed(); *separator = '\0'; - void* redirected_module = GetModuleHandleA(function_name); + void *redirected_module = GetModuleHandleA(function_name); if (!redirected_module) InterceptionFailed(); return InternalGetProcAddress(redirected_module, separator + 1); @@ -938,14 +928,13 @@ return 0; } -bool OverrideFunction( - const char *func_name, uptr new_func, uptr *orig_old_func) { +bool OverrideFunction(const char *func_name, uptr new_func, + uptr *orig_old_func) { bool hooked = false; void **DLLs = InterestingDLLsAvailable(); for (size_t i = 0; DLLs[i]; ++i) { uptr func_addr = InternalGetProcAddress(DLLs[i], func_name); - if (func_addr && - OverrideFunction(func_addr, new_func, orig_old_func)) { + if (func_addr && OverrideFunction(func_addr, new_func, orig_old_func)) { hooked = true; } } 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 @@ -28,6 +28,14 @@ // IMPORT: __asan_wrap_HeapFree // IMPORT: __asan_wrap_HeapReAlloc // IMPORT: __asan_wrap_HeapSize +// IMPORT: __asan_wrap_GlobalAlloc +// IMPORT: __asan_wrap_GlobalFree +// IMPORT: __asan_wrap_GlobalReAlloc +// IMPORT: __asan_wrap_GlobalSize +// IMPORT: __asan_wrap_LocalAlloc +// IMPORT: __asan_wrap_LocalFree +// IMPORT: __asan_wrap_LocalReAlloc +// IMPORT: __asan_wrap_LocalSize // IMPORT: __asan_wrap_CreateThread // IMPORT: __asan_wrap_RaiseException // IMPORT: __asan_wrap_RtlRaiseException diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc.cpp b/compiler-rt/test/asan/TestCases/Windows/globalalloc.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + +#include + +int main() { + char *buffer; + buffer = (char *)GlobalAlloc(GMEM_FIXED, 32), + buffer[33] = 'a'; + // CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] + // CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_doublefree.cpp b/compiler-rt/test/asan/TestCases/Windows/globalalloc_doublefree.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_doublefree.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + +#include +#include + +int main() { + void *allocation = GlobalAlloc(GMEM_FIXED, 10); + assert(allocation != 0); + assert(GlobalFree(allocation) == NULL); + GlobalFree(allocation); //will dump + assert(0 && "GlobalFree double free should produce an ASAN dump\n"); + return 0; +} + +// CHECK: AddressSanitizer: attempting double-free on [[addr:0x[0-9a-fA-F]+]] in thread T0: \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_flags_fallback.cpp b/compiler-rt/test/asan/TestCases/Windows/globalalloc_flags_fallback.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_flags_fallback.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits +#include +#include +#include + +extern "C" int +__sanitizer_get_ownership(const volatile void *p); + +int main() { + char *buffer; + HGLOBAL hMem; + hMem = GlobalAlloc(GMEM_MOVEABLE, 32); + buffer = (char *)GlobalLock(hMem); + buffer[0] = 'a'; + assert(!__sanitizer_get_ownership(buffer)); + GlobalUnlock(hMem); + GlobalFree(buffer); + puts("Okay"); + // CHECK: Okay +} diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_huge.cpp b/compiler-rt/test/asan/TestCases/Windows/globalalloc_huge.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_huge.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=allocator_may_return_null=true %run %t +// RUN: %env_asan_opts=allocator_may_return_null=true:windows_hook_rtl_allocators=true %run %t +// UNSUPPORTED: asan-64-bits +#include +#include +int main() { + void *nope = GlobalAlloc(GMEM_FIXED, ((size_t)0) - 1); + if (nope != nullptr) { + puts("Fail"); + return 1; + } + puts("Pass"); + return 0; +} +// CHECK: Pass +// CHECK-NOT: Fail \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_sanity.cpp b/compiler-rt/test/asan/TestCases/Windows/globalalloc_sanity.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_sanity.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +#include +#include + +int main() { + char *buffer; + buffer = (char *)GlobalAlloc(GMEM_FIXED, 32), + buffer[0] = 'a'; + GlobalFree(buffer); + puts("Okay"); + // CHECK: Okay +} diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_transfer.cpp b/compiler-rt/test/asan/TestCases/Windows/globalalloc_transfer.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_transfer.cpp @@ -0,0 +1,46 @@ +#include "sanitizer\allocator_interface.h" +#include +#include +#include +// RUN: %clang_cl_asan %s -o%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits + +int main() { + void *buffer, *realloc; + HGLOBAL hMem1, hMem2, hMem3; + + //owned by rtl + hMem1 = GlobalAlloc(GMEM_MOVEABLE, 100); + buffer = (void *)GlobalLock(hMem1); + assert(buffer); + + // still owned by rtl + hMem2 = GlobalReAlloc(buffer, 100, GMEM_MOVEABLE); + GlobalUnlock(hMem1); + buffer = (void *)GlobalLock(hMem2); + assert(buffer); + assert(!__sanitizer_get_ownership(buffer)); + assert(HeapValidate(GetProcessHeap(), 0, buffer)); + + //convert to asan owned + realloc = GlobalReAlloc(buffer, 500, GMEM_FIXED); + GlobalUnlock(hMem2); + buffer = nullptr; + assert(realloc); + assert(__sanitizer_get_ownership(realloc)); + + //convert back to rtl owned; + hMem3 = GlobalReAlloc(realloc, 100, GMEM_MOVEABLE); + buffer = (void *)GlobalLock(hMem3); + assert(buffer); + assert(!__sanitizer_get_ownership(buffer)); + assert(HeapValidate(GetProcessHeap(), 0, buffer)); + + GlobalUnlock(hMem3); + printf("Success\n"); +} + +// CHECK-NOT: assert +// CHECK-NOT: AddressSanitizer +// CHECK: Success diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_uaf.cpp b/compiler-rt/test/asan/TestCases/Windows/globalalloc_uaf.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_uaf.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + +#include + +int main() { + char *buffer; + buffer = (char *)GlobalAlloc(GMEM_FIXED, 32), + GlobalFree(buffer); + buffer[0] = 'a'; + // CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] + // CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} diff --git a/compiler-rt/test/asan/TestCases/Windows/globalalloc_zero_size.cpp b/compiler-rt/test/asan/TestCases/Windows/globalalloc_zero_size.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalalloc_zero_size.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan /Od -o %t %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=false %run %t 2>&1 | FileCheck %s +// RUN: %clang_cl /Od -o %t %s +// RUN: %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits +#include +#include +#include +#include + +int main() { + void *ptr = GlobalAlloc(GMEM_FIXED, 4); + assert(ptr); + void *ptr2 = GlobalReAlloc(ptr, 0, GMEM_ZEROINIT); + assert(ptr2); + GlobalFree(ptr2); + fprintf(stderr, "passed!\n"); +} + +// CHECK-NOT: double-free +// CHECK-NOT: AddressSanitizer +// CHECK: passed! \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/globalrealloc.cpp b/compiler-rt/test/asan/TestCases/Windows/globalrealloc.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/globalrealloc.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits +#include +#include +#include + +int main() { + char *oldbuf; + size_t sz = 8; + oldbuf = (char *)GlobalAlloc(GMEM_FIXED, sz); + char *newbuf = oldbuf; + while (oldbuf == newbuf) { + sz *= 2; + newbuf = (char *)GlobalReAlloc(oldbuf, sz, GMEM_ZEROINIT); + } + + newbuf[0] = 'a'; + oldbuf[0] = 'a'; + // CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] + // CHECK: WRITE of size 1 at [[WRITE2:0x[0-9a-f]+]] thread T0 + // CHECK: #0 {{0x[0-9a-f]+ in main.*}}:[[@LINE-3]] +} diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_freenull.cc b/compiler-rt/test/asan/TestCases/Windows/heapalloc_freenull.cc new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/heapalloc_freenull.cc @@ -0,0 +1,13 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 +// UNSUPPORTED: asan-64-bits +#include +#include + +int main() { + void *allocation = HeapAlloc(GetProcessHeap(), 0, 10); + assert(allocation != 0); + HeapFree(GetProcessHeap(), 0, (void *)0); + //TODO: This should actually trigger a report, since Free(NULL) is undefined for windows. + return 0; +} diff --git a/compiler-rt/test/asan/TestCases/Windows/heapalloc_huge.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 @@ -2,6 +2,7 @@ // RUN: %env_asan_opts=allocator_may_return_null=true %run %t // RUN: %env_asan_opts=allocator_may_return_null=true:windows_hook_rtl_allocators=true %run %t // UNSUPPORTED: asan-64-bits +#include #include int main() { void *nope = HeapAlloc(GetProcessHeap(), 0, ((size_t)0) - 1); diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc.cpp b/compiler-rt/test/asan/TestCases/Windows/localalloc.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + +#include + +int main() { + char *buffer; + buffer = (char *)LocalAlloc(LMEM_FIXED, 32), + buffer[33] = 'a'; + // CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] + // CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc_doublefree.cpp b/compiler-rt/test/asan/TestCases/Windows/localalloc_doublefree.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc_doublefree.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + +#include +#include + +int main() { + void *allocation = LocalAlloc(LMEM_FIXED, 10); + assert(allocation != 0); + assert(LocalFree(allocation) == NULL); + LocalFree(allocation); //will dump + assert(0 && "LocalFree double free should produce an ASAN dump\n"); + return 0; +} + +// CHECK: AddressSanitizer: attempting double-free on [[addr:0x[0-9a-fA-F]+]] in thread T0: \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc_flags_fallback.cpp b/compiler-rt/test/asan/TestCases/Windows/localalloc_flags_fallback.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc_flags_fallback.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits +#include +#include +#include + +extern "C" int +__sanitizer_get_ownership(const volatile void *p); + +int main() { + char *buffer; + HGLOBAL hMem; + hMem = LocalAlloc(LMEM_MOVEABLE, 32); + buffer = (char *)LocalLock(hMem); + buffer[0] = 'a'; + assert(!__sanitizer_get_ownership(buffer)); + LocalUnlock(hMem); + LocalFree(buffer); + puts("Okay"); + // CHECK: Okay +} \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc_huge.cpp b/compiler-rt/test/asan/TestCases/Windows/localalloc_huge.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc_huge.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=allocator_may_return_null=true %run %t +// RUN: %env_asan_opts=allocator_may_return_null=true:windows_hook_rtl_allocators=true %run %t +// UNSUPPORTED: asan-64-bits +#include +int main() { + void *nope = LocalAlloc(LMEM_FIXED, ((size_t)0) - 1); + return nope != nullptr; +} \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc_sanity.cpp b/compiler-rt/test/asan/TestCases/Windows/localalloc_sanity.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc_sanity.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +#include +#include + +int main() { + char *buffer; + buffer = (char *)LocalAlloc(LMEM_FIXED, 32), + buffer[0] = 'a'; + LocalFree(buffer); + puts("Okay"); + // CHECK: Okay +} diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc_uaf.cpp b/compiler-rt/test/asan/TestCases/Windows/localalloc_uaf.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc_uaf.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s + +#include + +int main() { + char *buffer; + buffer = (char *)LocalAlloc(LMEM_FIXED, 32), + LocalFree(buffer); + buffer[0] = 'a'; + // CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] + // CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} diff --git a/compiler-rt/test/asan/TestCases/Windows/localalloc_zero_size.cpp b/compiler-rt/test/asan/TestCases/Windows/localalloc_zero_size.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localalloc_zero_size.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan /Od -o %t %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=false %run %t 2>&1 | FileCheck %s +// RUN: %clang_cl /Od -o %t %s +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits +#include +#include +#include +#include + +int main() { + void *ptr = LocalAlloc(LMEM_FIXED, 4); + assert(ptr); + void *ptr2 = LocalReAlloc(ptr, 0, LMEM_ZEROINIT); + assert(ptr2); + LocalFree(ptr2); + fprintf(stderr, "passed!\n"); +} + +// CHECK-NOT: double-free +// CHECK-NOT: AddressSanitizer +// CHECK: passed! \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/localrealloc.cpp b/compiler-rt/test/asan/TestCases/Windows/localrealloc.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localrealloc.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan /Od %s -Fe%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits +#include +#include +#include + +int main() { + char *oldbuf; + size_t sz = 8; + oldbuf = (char *)LocalAlloc(LMEM_FIXED, sz); + char *newbuf = oldbuf; + while (oldbuf == newbuf) { + sz *= 2; + newbuf = (char *)LocalReAlloc(oldbuf, sz, LMEM_ZEROINIT); + } + + newbuf[0] = 'a'; + oldbuf[0] = 'a'; + // CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] + // CHECK: WRITE of size 1 at [[WRITE2:0x[0-9a-f]+]] thread T0 + // CHECK: #0 {{0x[0-9a-f]+ in main.*}}:[[@LINE-3]] +} \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/Windows/localrealloc_transfer.cpp b/compiler-rt/test/asan/TestCases/Windows/localrealloc_transfer.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Windows/localrealloc_transfer.cpp @@ -0,0 +1,46 @@ +#include "sanitizer\allocator_interface.h" +#include +#include +#include +// RUN: %clang_cl_asan %s -o%t +// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: asan-64-bits + +int main() { + void *buffer, *realloc; + HLOCAL hMem1, hMem2, hMem3; + + //owned by rtl + hMem1 = LocalAlloc(LMEM_MOVEABLE, 100); + buffer = (void *)LocalLock(hMem1); + assert(buffer); + + // still owned by rtl + hMem2 = LocalReAlloc(buffer, 100, LMEM_MOVEABLE); + LocalUnlock(hMem1); + buffer = (void *)LocalLock(hMem2); + assert(buffer); + assert(!__sanitizer_get_ownership(buffer)); + assert(HeapValidate(GetProcessHeap(), 0, buffer)); + + //convert to asan owned + realloc = LocalReAlloc(buffer, 500, LMEM_FIXED); + LocalUnlock(hMem2); + buffer = nullptr; + assert(realloc); + assert(__sanitizer_get_ownership(realloc)); + + //convert back to rtl owned; + hMem3 = LocalReAlloc(realloc, 100, LMEM_MOVEABLE); + buffer = (void *)LocalLock(hMem3); + assert(buffer); + assert(!__sanitizer_get_ownership(buffer)); + assert(HeapValidate(GetProcessHeap(), 0, buffer)); + + LocalUnlock(hMem3); + printf("Success\n"); +} + +// CHECK-NOT: assert +// CHECK-NOT: AddressSanitizer +// CHECK: Success