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