summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew G McGovern <matthew.mcgovern@microsoft.com>2019-07-09 01:47:08 +0000
committerMatthew G McGovern <matthew.mcgovern@microsoft.com>2019-07-09 01:47:08 +0000
commit029fefe7e4e62cbb33cf1d0f0e7a84419d8d7a18 (patch)
treeed383723621a72f5bee0ab329e148831f959bd2f
parent4a86a652ce68eba9145170f9bc64c9c3ac776b30 (diff)
downloadcompiler-rt-029fefe7e4e62cbb33cf1d0f0e7a84419d8d7a18.tar.gz
[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 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@365422 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/asan/asan_flags.inc2
-rw-r--r--lib/asan/asan_malloc_win.cc309
-rw-r--r--lib/asan/asan_win.cc75
-rw-r--r--test/asan/TestCases/Windows/dll_host.cc4
-rw-r--r--test/asan/TestCases/Windows/dll_unload.cc51
-rw-r--r--test/asan/TestCases/Windows/heapalloc.cc13
-rw-r--r--test/asan/TestCases/Windows/heapalloc_dll_double_free.cc40
-rw-r--r--test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cc40
-rw-r--r--test/asan/TestCases/Windows/heapalloc_doublefree.cc16
-rw-r--r--test/asan/TestCases/Windows/heapalloc_flags_fallback.cc20
-rw-r--r--test/asan/TestCases/Windows/heapalloc_huge.cc9
-rw-r--r--test/asan/TestCases/Windows/heapalloc_rtl_transfer.cc98
-rw-r--r--test/asan/TestCases/Windows/heapalloc_sanity.cc13
-rw-r--r--test/asan/TestCases/Windows/heapalloc_transfer.cc28
-rw-r--r--test/asan/TestCases/Windows/heapalloc_uaf.cc13
-rw-r--r--test/asan/TestCases/Windows/heapalloc_zero_size.cc23
-rw-r--r--test/asan/TestCases/Windows/heaprealloc.cc23
-rw-r--r--test/asan/TestCases/Windows/heaprealloc_alloc_zero.cc58
-rw-r--r--test/asan/TestCases/Windows/heaprealloc_zero_size.cc4
-rw-r--r--test/asan/TestCases/Windows/queue_user_work_item_report.cc6
-rw-r--r--test/asan/TestCases/Windows/rtlallocateheap.cc30
-rw-r--r--test/asan/TestCases/Windows/rtlallocateheap_dll_unload_double_free.cc72
-rw-r--r--test/asan/TestCases/Windows/rtlallocateheap_dll_unload_realloc.cc76
-rw-r--r--test/asan/TestCases/Windows/rtlallocateheap_flags_fallback.cc45
-rw-r--r--test/asan/TestCases/Windows/rtlallocateheap_zero.cc68
25 files changed, 1081 insertions, 55 deletions
diff --git a/lib/asan/asan_flags.inc b/lib/asan/asan_flags.inc
index 3c30a6283..d360e03ca 100644
--- a/lib/asan/asan_flags.inc
+++ b/lib/asan/asan_flags.inc
@@ -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.")
diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc
index b13d798a3..4586beb09 100644
--- a/lib/asan/asan_malloc_win.cc
+++ b/lib/asan/asan_malloc_win.cc
@@ -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
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index c9b81fa8f..f7601f330 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -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)
diff --git a/test/asan/TestCases/Windows/dll_host.cc b/test/asan/TestCases/Windows/dll_host.cc
index 512f930bc..9a4181541 100644
--- a/test/asan/TestCases/Windows/dll_host.cc
+++ b/test/asan/TestCases/Windows/dll_host.cc
@@ -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
diff --git a/test/asan/TestCases/Windows/dll_unload.cc b/test/asan/TestCases/Windows/dll_unload.cc
new file mode 100644
index 000000000..055567a0f
--- /dev/null
+++ b/test/asan/TestCases/Windows/dll_unload.cc
@@ -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
+}
diff --git a/test/asan/TestCases/Windows/heapalloc.cc b/test/asan/TestCases/Windows/heapalloc.cc
new file mode 100644
index 000000000..66322a3e7
--- /dev/null
+++ b/test/asan/TestCases/Windows/heapalloc.cc
@@ -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
+}
diff --git a/test/asan/TestCases/Windows/heapalloc_dll_double_free.cc b/test/asan/TestCases/Windows/heapalloc_dll_double_free.cc
new file mode 100644
index 000000000..496289ae1
--- /dev/null
+++ b/test/asan/TestCases/Windows/heapalloc_dll_double_free.cc
@@ -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
+}
diff --git a/test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cc b/test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cc
new file mode 100644
index 000000000..79b30f4a9
--- /dev/null
+++ b/test/asan/TestCases/Windows/heapalloc_dll_unload_realloc_uaf.cc
@@ -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
+}
diff --git a/test/asan/TestCases/Windows/heapalloc_doublefree.cc b/test/asan/TestCases/Windows/heapalloc_doublefree.cc
new file mode 100644
index 000000000..dcdf98932
--- /dev/null
+++ b/test/asan/TestCases/Windows/heapalloc_doublefree.cc
@@ -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: \ No newline at end of file
diff --git a/test/asan/TestCases/Windows/heapalloc_flags_fallback.cc b/test/asan/TestCases/Windows/heapalloc_flags_fallback.cc
new file mode 100644
index 000000000..eda94ac00
--- /dev/null
+++ b/test/asan/TestCases/Windows/heapalloc_flags_fallback.cc
@@ -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
+}
diff --git a/test/asan/TestCases/Windows/heapalloc_huge.cc b/test/asan/TestCases/Windows/heapalloc_huge.cc
new file mode 100644
index 000000000..ce1fd6562
--- /dev/null
+++ b/test/asan/TestCases/Windows/heapalloc_huge.cc
@@ -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;
+} \ No newline at end of file
diff --git a/test/asan/TestCases/Windows/heapalloc_rtl_transfer.cc b/test/asan/TestCases/Windows/heapalloc_rtl_transfer.cc
new file mode 100644
index 000000000..b621270d7
--- /dev/null
+++ b/test/asan/TestCases/Windows/heapalloc_rtl_transfer.cc
@@ -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 \ No newline at end of file
diff --git a/test/asan/TestCases/Windows/heapalloc_sanity.cc b/test/asan/TestCases/Windows/heapalloc_sanity.cc
new file mode 100644
index 000000000..5dfc14b72
--- /dev/null
+++ b/test/asan/TestCases/Windows/heapalloc_sanity.cc
@@ -0,0 +1,13 @@
+// RUN: %clang_cl_asan -O0 %s -Fe%t
+// RUN: %run %t 2>&1 | FileCheck %s
+#include <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
+}
diff --git a/test/asan/TestCases/Windows/heapalloc_transfer.cc b/test/asan/TestCases/Windows/heapalloc_transfer.cc
new file mode 100644
index 000000000..b3b5746d6
--- /dev/null
+++ b/test/asan/TestCases/Windows/heapalloc_transfer.cc
@@ -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 \ No newline at end of file
diff --git a/test/asan/TestCases/Windows/heapalloc_uaf.cc b/test/asan/TestCases/Windows/heapalloc_uaf.cc
new file mode 100644
index 000000000..00fb3ab6f
--- /dev/null
+++ b/test/asan/TestCases/Windows/heapalloc_uaf.cc
@@ -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
+}
diff --git a/test/asan/TestCases/Windows/heapalloc_zero_size.cc b/test/asan/TestCases/Windows/heapalloc_zero_size.cc
new file mode 100644
index 000000000..23fc97b20
--- /dev/null
+++ b/test/asan/TestCases/Windows/heapalloc_zero_size.cc
@@ -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!
diff --git a/test/asan/TestCases/Windows/heaprealloc.cc b/test/asan/TestCases/Windows/heaprealloc.cc
new file mode 100644
index 000000000..e6efb87b5
--- /dev/null
+++ b/test/asan/TestCases/Windows/heaprealloc.cc
@@ -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]]
+}
diff --git a/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cc b/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cc
new file mode 100644
index 000000000..76ae89690
--- /dev/null
+++ b/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cc
@@ -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! \ No newline at end of file
diff --git a/test/asan/TestCases/Windows/heaprealloc_zero_size.cc b/test/asan/TestCases/Windows/heaprealloc_zero_size.cc
index 3d60202ce..ad2478b19 100644
--- a/test/asan/TestCases/Windows/heaprealloc_zero_size.cc
+++ b/test/asan/TestCases/Windows/heaprealloc_zero_size.cc
@@ -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>
diff --git a/test/asan/TestCases/Windows/queue_user_work_item_report.cc b/test/asan/TestCases/Windows/queue_user_work_item_report.cc
index 26bd5e09d..27c438a56 100644
--- a/test/asan/TestCases/Windows/queue_user_work_item_report.cc
+++ b/test/asan/TestCases/Windows/queue_user_work_item_report.cc
@@ -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;
}
diff --git a/test/asan/TestCases/Windows/rtlallocateheap.cc b/test/asan/TestCases/Windows/rtlallocateheap.cc
new file mode 100644
index 000000000..5831fe27b
--- /dev/null
+++ b/test/asan/TestCases/Windows/rtlallocateheap.cc
@@ -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
+}
diff --git a/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_double_free.cc b/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_double_free.cc
new file mode 100644
index 000000000..078114506
--- /dev/null
+++ b/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_double_free.cc
@@ -0,0 +1,72 @@
+// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll
+// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe
+// RUN: %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
+}
diff --git a/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_realloc.cc b/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_realloc.cc
new file mode 100644
index 000000000..a2758cad2
--- /dev/null
+++ b/test/asan/TestCases/Windows/rtlallocateheap_dll_unload_realloc.cc
@@ -0,0 +1,76 @@
+// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll
+// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe
+// RUN: %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
+}
diff --git a/test/asan/TestCases/Windows/rtlallocateheap_flags_fallback.cc b/test/asan/TestCases/Windows/rtlallocateheap_flags_fallback.cc
new file mode 100644
index 000000000..c377fe360
--- /dev/null
+++ b/test/asan/TestCases/Windows/rtlallocateheap_flags_fallback.cc
@@ -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
+}
diff --git a/test/asan/TestCases/Windows/rtlallocateheap_zero.cc b/test/asan/TestCases/Windows/rtlallocateheap_zero.cc
new file mode 100644
index 000000000..d4bde7857
--- /dev/null
+++ b/test/asan/TestCases/Windows/rtlallocateheap_zero.cc
@@ -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
+}