[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:
Matthew G McGovern 2019-07-08 19:58:50 +00:00
parent 967aa5745d
commit c9fa99d066
25 changed files with 1081 additions and 55 deletions

View File

@ -158,3 +158,5 @@ ASAN_FLAG(bool, allocator_frees_and_returns_null_on_realloc_zero, true,
ASAN_FLAG(bool, verify_asan_link_order, true,
"Check position of ASan runtime in library list (needs to be disabled"
" when other library has to be preloaded system-wide)")
ASAN_FLAG(bool, windows_hook_rtl_allocators, false,
"(Windows only) enable hooking of Rtl(Allocate|Free|Size|ReAllocate)Heap.")

View File

@ -11,6 +11,19 @@
// 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"
#if SANITIZER_WINDOWS
// Intentionally not including windows.h here, to avoid the risk of
@ -21,9 +34,16 @@ typedef void *HANDLE;
typedef const void *LPCVOID;
typedef void *LPVOID;
#define HEAP_ZERO_MEMORY 0x00000008
#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010
constexpr unsigned long HEAP_ALLOCATE_SUPPORTED_FLAGS = (HEAP_ZERO_MEMORY);
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_interceptors.h"
@ -183,45 +203,176 @@ int _CrtSetReportMode(int, int) {
}
} // extern "C"
#define OWNED_BY_RTL(heap, memory) \
(!__sanitizer_get_ownership(memory) && HeapValidate(heap, 0, memory))
INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags,
LPCVOID lpMem) {
// If the RTL allocators are hooked we need to check whether the ASAN
// allocator owns the pointer we're about to use. Allocations occur before
// interception takes place, so if it is not owned by the RTL heap we can
// pass it to the ASAN heap for inspection.
if (flags()->windows_hook_rtl_allocators) {
if (!asan_inited || OWNED_BY_RTL(hHeap, lpMem))
return REAL(HeapSize)(hHeap, dwFlags, lpMem);
} else {
CHECK(dwFlags == 0 && "unsupported heap flags");
}
GET_CURRENT_PC_BP_SP;
(void)sp;
return asan_malloc_usable_size(lpMem, pc, bp);
}
INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags,
SIZE_T dwBytes) {
// If the ASAN runtime is not initialized, or we encounter an unsupported
// flag, fall back to the original allocator.
if (flags()->windows_hook_rtl_allocators) {
if (UNLIKELY(!asan_inited ||
(dwFlags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) {
return REAL(HeapAlloc)(hHeap, dwFlags, dwBytes);
}
} else {
// In the case that we don't hook the rtl allocators,
// this becomes an assert since there is no failover to the original
// allocator.
CHECK((HEAP_ALLOCATE_UNSUPPORTED_FLAGS & dwFlags) != 0 &&
"unsupported flags");
}
GET_STACK_TRACE_MALLOC;
void *p = asan_malloc(dwBytes, &stack);
// Reading MSDN suggests that the *entire* usable allocation is zeroed out.
// Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY.
// https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083
if (dwFlags == HEAP_ZERO_MEMORY)
internal_memset(p, 0, asan_mz_size(p));
else
CHECK(dwFlags == 0 && "unsupported heap flags");
if (p && (dwFlags & HEAP_ZERO_MEMORY)) {
GET_CURRENT_PC_BP_SP;
(void)sp;
auto usable_size = asan_malloc_usable_size(p, pc, bp);
internal_memset(p, 0, usable_size);
}
return p;
}
INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) {
CHECK(dwFlags == 0 && "unsupported heap flags");
// Heap allocations happen before this function is hooked, so we must fall
// back to the original function if the pointer is not from the ASAN heap,
// or unsupported flags are provided.
if (flags()->windows_hook_rtl_allocators) {
if (OWNED_BY_RTL(hHeap, lpMem))
return REAL(HeapFree)(hHeap, dwFlags, lpMem);
} else {
CHECK((HEAP_FREE_UNSUPPORTED_FLAGS & dwFlags) != 0 && "unsupported flags");
}
GET_STACK_TRACE_FREE;
asan_free(lpMem, &stack, FROM_MALLOC);
return true;
}
INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags,
LPVOID lpMem, SIZE_T dwBytes) {
namespace __asan {
using AllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, SIZE_T);
using ReAllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, LPVOID, SIZE_T);
using SizeFunction = SIZE_T(WINAPI *)(HANDLE, DWORD, LPVOID);
using FreeFunction = BOOL(WINAPI *)(HANDLE, DWORD, LPVOID);
void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc,
FreeFunction freeFunc, AllocFunction allocFunc,
HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes) {
CHECK(reallocFunc && heapSizeFunc && freeFunc && allocFunc);
GET_STACK_TRACE_MALLOC;
GET_CURRENT_PC_BP_SP;
(void)sp;
// 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)
return nullptr;
CHECK(dwFlags == 0 && "unsupported heap flags");
// HeapReAlloc and HeapAlloc both happily accept 0 sized allocations.
// passing a 0 size into asan_realloc will free the allocation.
// To avoid this and keep behavior consistent, fudge the size if 0.
// (asan_malloc already does this)
if (dwBytes == 0)
dwBytes = 1;
size_t old_size;
if (dwFlags & HEAP_ZERO_MEMORY)
old_size = asan_malloc_usable_size(lpMem, pc, bp);
void *ptr = asan_realloc(lpMem, dwBytes, &stack);
if (ptr == nullptr)
return nullptr;
@ -231,15 +382,101 @@ INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags,
if (old_size < new_size)
REAL(memset)(((u8 *)ptr) + old_size, 0, new_size - old_size);
}
return ptr;
}
} // namespace __asan
INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags,
LPCVOID lpMem) {
CHECK(dwFlags == 0 && "unsupported heap flags");
INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags,
LPVOID lpMem, SIZE_T dwBytes) {
return SharedReAlloc(REAL(HeapReAlloc), (SizeFunction)REAL(HeapSize),
REAL(HeapFree), REAL(HeapAlloc), hHeap, dwFlags, lpMem,
dwBytes);
}
// The following functions are undocumented and subject to change.
// However, hooking them is necessary to hook Windows heap
// allocations with detours and their definitions are unlikely to change.
// Comments in /minkernel/ntos/rtl/heappublic.c indicate that these functions
// are part of the heap's public interface.
typedef ULONG LOGICAL;
// This function is documented as part of the Driver Development Kit but *not*
// the Windows Development Kit.
NTSYSAPI LOGICAL RtlFreeHeap(PVOID HeapHandle, ULONG Flags,
_Frees_ptr_opt_ PVOID BaseAddress);
// This function is documented as part of the Driver Development Kit but *not*
// the Windows Development Kit.
NTSYSAPI PVOID RtlAllocateHeap(PVOID HeapHandle, ULONG Flags, SIZE_T Size);
// This function is completely undocumented.
PVOID
RtlReAllocateHeap(PVOID HeapHandle, ULONG Flags, PVOID BaseAddress,
SIZE_T Size);
// This function is completely undocumented.
SIZE_T
RtlSizeHeap(PVOID HeapHandle, ULONG Flags, PVOID BaseAddress);
INTERCEPTOR_WINAPI(SIZE_T, RtlSizeHeap, HANDLE HeapHandle, ULONG Flags,
PVOID BaseAddress) {
if (!flags()->windows_hook_rtl_allocators ||
UNLIKELY(!asan_inited || OWNED_BY_RTL(HeapHandle, BaseAddress))) {
return REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress);
}
GET_CURRENT_PC_BP_SP;
(void)sp;
return asan_malloc_usable_size(lpMem, pc, bp);
return asan_malloc_usable_size(BaseAddress, pc, bp);
}
INTERCEPTOR_WINAPI(BOOL, RtlFreeHeap, HANDLE HeapHandle, ULONG Flags,
PVOID BaseAddress) {
// Heap allocations happen before this function is hooked, so we must fall
// back to the original function if the pointer is not from the ASAN heap, or
// unsupported flags are provided.
if (!flags()->windows_hook_rtl_allocators ||
UNLIKELY((HEAP_FREE_UNSUPPORTED_FLAGS & Flags) != 0 ||
OWNED_BY_RTL(HeapHandle, BaseAddress))) {
return REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress);
}
GET_STACK_TRACE_FREE;
asan_free(BaseAddress, &stack, FROM_MALLOC);
return true;
}
INTERCEPTOR_WINAPI(PVOID, RtlAllocateHeap, HANDLE HeapHandle, DWORD Flags,
SIZE_T Size) {
// If the ASAN runtime is not initialized, or we encounter an unsupported
// flag, fall back to the original allocator.
if (!flags()->windows_hook_rtl_allocators ||
UNLIKELY(!asan_inited ||
(Flags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) {
return REAL(RtlAllocateHeap)(HeapHandle, Flags, Size);
}
GET_STACK_TRACE_MALLOC;
void *p;
// Reading MSDN suggests that the *entire* usable allocation is zeroed out.
// Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY.
// https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083
if (Flags & HEAP_ZERO_MEMORY) {
p = asan_calloc(Size, 1, &stack);
} else {
p = asan_malloc(Size, &stack);
}
return p;
}
INTERCEPTOR_WINAPI(PVOID, RtlReAllocateHeap, HANDLE HeapHandle, ULONG Flags,
PVOID BaseAddress, SIZE_T Size) {
// If it's actually a heap block which was allocated before the ASAN runtime
// came up, use the real RtlFreeHeap function.
if (!flags()->windows_hook_rtl_allocators)
return REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size);
return SharedReAlloc(REAL(RtlReAllocateHeap), REAL(RtlSizeHeap),
REAL(RtlFreeHeap), REAL(RtlAllocateHeap), HeapHandle,
Flags, BaseAddress, Size);
}
namespace __asan {
@ -272,6 +509,34 @@ void ReplaceSystemMalloc() {
TryToOverrideFunction("_expand", (uptr)_expand);
TryToOverrideFunction("_expand_base", (uptr)_expand);
if (flags()->windows_hook_rtl_allocators) {
INTERCEPT_FUNCTION(HeapSize);
INTERCEPT_FUNCTION(HeapFree);
INTERCEPT_FUNCTION(HeapReAlloc);
INTERCEPT_FUNCTION(HeapAlloc);
// Undocumented functions must be intercepted by name, not by symbol.
__interception::OverrideFunction("RtlSizeHeap", (uptr)WRAP(RtlSizeHeap),
(uptr *)&REAL(RtlSizeHeap));
__interception::OverrideFunction("RtlFreeHeap", (uptr)WRAP(RtlFreeHeap),
(uptr *)&REAL(RtlFreeHeap));
__interception::OverrideFunction("RtlReAllocateHeap",
(uptr)WRAP(RtlReAllocateHeap),
(uptr *)&REAL(RtlReAllocateHeap));
__interception::OverrideFunction("RtlAllocateHeap",
(uptr)WRAP(RtlAllocateHeap),
(uptr *)&REAL(RtlAllocateHeap));
} else {
#define INTERCEPT_UCRT_FUNCTION(func) \
if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll", \
"api-ms-win-core-heap-l1-1-0.dll", func)) \
VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func);
INTERCEPT_UCRT_FUNCTION(HeapAlloc);
INTERCEPT_UCRT_FUNCTION(HeapFree);
INTERCEPT_UCRT_FUNCTION(HeapReAlloc);
INTERCEPT_UCRT_FUNCTION(HeapSize);
#undef INTERCEPT_UCRT_FUNCTION
}
// Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which
// enable cross-module inlining. This means our _malloc_base hook won't catch
// all CRT allocations. This code here patches the import table of
@ -279,16 +544,8 @@ void ReplaceSystemMalloc() {
// allocation API will be directed to ASan's heap. We don't currently
// intercept all calls to HeapAlloc. If we did, we would have to check on
// HeapFree whether the pointer came from ASan of from the system.
#define INTERCEPT_UCRT_FUNCTION(func) \
if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll", \
"api-ms-win-core-heap-l1-1-0.dll", func)) \
VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func);
INTERCEPT_UCRT_FUNCTION(HeapAlloc);
INTERCEPT_UCRT_FUNCTION(HeapFree);
INTERCEPT_UCRT_FUNCTION(HeapReAlloc);
INTERCEPT_UCRT_FUNCTION(HeapSize);
#undef INTERCEPT_UCRT_FUNCTION
#endif
#endif // defined(ASAN_DYNAMIC)
}
} // namespace __asan

View File

@ -20,10 +20,10 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_thread.h"
#include "asan_mapping.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_mutex.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,
LPTOP_LEVEL_EXCEPTION_FILTER ExceptionFilter) {
LPTOP_LEVEL_EXCEPTION_FILTER ExceptionFilter) {
CHECK(REAL(SetUnhandledExceptionFilter));
if (ExceptionFilter == &SEHHandler)
return REAL(SetUnhandledExceptionFilter)(ExceptionFilter);
@ -132,7 +132,7 @@ INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
#endif
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
AsanThread *t = (AsanThread*)arg;
AsanThread *t = (AsanThread *)arg;
SetCurrentThread(t);
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
// module alive for the life of the process.
HMODULE pinned;
CHECK(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_PIN,
(LPCWSTR)&InitializePlatformInterceptors,
&pinned));
CHECK(GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
(LPCWSTR)&InitializePlatformInterceptors, &pinned));
ASAN_INTERCEPT_FUNC(CreateThread);
ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter);
@ -197,6 +196,30 @@ static bool tsd_key_inited = false;
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)) {
// FIXME: we're ignoring the destructor for now.
tsd_key_inited = true;
@ -204,7 +227,7 @@ void AsanTSDInit(void (*destructor)(void *tsd)) {
void *AsanTSDGet() {
CHECK(tsd_key_inited);
return fake_tsd;
return IsTlsInitialized() ? fake_tsd : nullptr;
}
void AsanTSDSet(void *tsd) {
@ -212,9 +235,7 @@ void AsanTSDSet(void *tsd) {
fake_tsd = tsd;
}
void PlatformTSDDtor(void *tsd) {
AsanThread::TSDDtor(tsd);
}
void PlatformTSDDtor(void *tsd) { AsanThread::TSDDtor(tsd); }
// }}}
// ---------------------- Various stuff ---------------- {{{
@ -245,9 +266,7 @@ void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
UNIMPLEMENTED();
}
void AsanOnDeadlySignal(int, void *siginfo, void *context) {
UNIMPLEMENTED();
}
void AsanOnDeadlySignal(int, void *siginfo, void *context) { UNIMPLEMENTED(); }
#if SANITIZER_WINDOWS64
// Exception handler for dealing with shadow memory.
@ -256,7 +275,9 @@ ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
uptr page_size = GetPageSizeCached();
// Only handle access violations.
if (exception_pointers->ExceptionRecord->ExceptionCode !=
EXCEPTION_ACCESS_VIOLATION) {
EXCEPTION_ACCESS_VIOLATION ||
exception_pointers->ExceptionRecord->NumberParameters < 2) {
__asan_handle_no_return();
return EXCEPTION_CONTINUE_SEARCH;
}
@ -265,7 +286,10 @@ ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
(uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]);
// Check valid shadow range.
if (!AddrIsInShadow(addr)) return EXCEPTION_CONTINUE_SEARCH;
if (!AddrIsInShadow(addr)) {
__asan_handle_no_return();
return EXCEPTION_CONTINUE_SEARCH;
}
// This is an access violation while trying to read from the shadow. Commit
// the relevant page and let execution continue.
@ -276,7 +300,8 @@ ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
// Commit the page.
uptr result =
(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.
return EXCEPTION_CONTINUE_EXECUTION;
@ -293,7 +318,7 @@ void InitializePlatformExceptionHandlers() {
}
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
@ -312,8 +337,7 @@ bool IsSystemHeapAddress(uptr addr) {
// 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
// __asan_set_seh_filter call happens after the .exe module CRT is initialized.
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
int __asan_set_seh_filter() {
extern "C" SANITIZER_INTERFACE_ATTRIBUTE int __asan_set_seh_filter() {
// We should only store the previous handler if it's not our own handler in
// order to avoid loops in the EH chain.
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
// to run our initializers first.
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
__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *,
unsigned long, void *) = asan_thread_init;
__declspec(allocate(".CRT$XLAB")) void(NTAPI *__asan_tls_init)(
void *, unsigned long, void *) = asan_thread_init;
#endif
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
__declspec(allocate(".CRT$XLY")) void (NTAPI *__asan_tls_exit)(void *,
unsigned long, void *) = asan_thread_exit;
__declspec(allocate(".CRT$XLY")) void(NTAPI *__asan_tls_exit)(
void *, unsigned long, void *) = asan_thread_exit;
WIN_FORCE_LINK(__asan_dso_reg_hook)

View File

@ -32,6 +32,10 @@
// IMPORT: __asan_wrap_RaiseException
// IMPORT: __asan_wrap_RtlRaiseException
// IMPORT: __asan_wrap_SetUnhandledExceptionFilter
// IMPORT: __asan_wrap_RtlSizeHeap
// IMPORT: __asan_wrap_RtlAllocateHeap
// IMPORT: __asan_wrap_RtlReAllocateHeap
// IMPORT: __asan_wrap_RtlFreeHeap
//
// RUN: cat %t.imports1 %t.imports2 | sort | uniq > %t.imports-sorted
// RUN: cat %t.exports1 %t.exports2 | sort | uniq > %t.exports-sorted

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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:

View File

@ -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
}

View File

@ -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;
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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!

View File

@ -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]]
}

View File

@ -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!

View File

@ -1,7 +1,9 @@
// RUN: %clang_cl_asan /Od -o %t %s
// RUN: %run %t 2>&1 | FileCheck %s
// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s
// RUN: %env_asan_opts=windows_hook_rtl_allocators=false %run %t 2>&1 | FileCheck %s
// RUN: %clang_cl /Od -o %t %s
// RUN: %run %t 2>&1 | FileCheck %s
// XFAIL: asan-64-bits
#include <cassert>
#include <stdio.h>
#include<windows.h>

View File

@ -9,9 +9,9 @@ DWORD CALLBACK work_item(LPVOID) {
int subscript = -1;
volatile char stack_buffer[42];
stack_buffer[subscript] = 42;
// CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]]
// CHECK: WRITE of size 1 at [[ADDR]] thread T1
// CHECK: {{#0 .* work_item.*queue_user_work_item_report.cc}}:[[@LINE-3]]
// CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]]
// CHECK: WRITE of size 1 at [[ADDR]] thread T{{[0-9]+}}
// CHECK: {{#0 .* work_item.*queue_user_work_item_report.cc}}:[[@LINE-3]]
SetEvent(done);
return 0;
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}