1*68d75effSDimitry Andric //===-- asan_malloc_win.cpp -----------------------------------------------===// 2*68d75effSDimitry Andric // 3*68d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*68d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*68d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*68d75effSDimitry Andric // 7*68d75effSDimitry Andric //===----------------------------------------------------------------------===// 8*68d75effSDimitry Andric // 9*68d75effSDimitry Andric // This file is a part of AddressSanitizer, an address sanity checker. 10*68d75effSDimitry Andric // 11*68d75effSDimitry Andric // Windows-specific malloc interception. 12*68d75effSDimitry Andric //===----------------------------------------------------------------------===// 13*68d75effSDimitry Andric 14*68d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_interface.h" 15*68d75effSDimitry Andric #include "sanitizer_common/sanitizer_platform.h" 16*68d75effSDimitry Andric #if SANITIZER_WINDOWS 17*68d75effSDimitry Andric #include "asan_allocator.h" 18*68d75effSDimitry Andric #include "asan_interceptors.h" 19*68d75effSDimitry Andric #include "asan_internal.h" 20*68d75effSDimitry Andric #include "asan_stack.h" 21*68d75effSDimitry Andric #include "interception/interception.h" 22*68d75effSDimitry Andric #include <stddef.h> 23*68d75effSDimitry Andric 24*68d75effSDimitry Andric // Intentionally not including windows.h here, to avoid the risk of 25*68d75effSDimitry Andric // pulling in conflicting declarations of these functions. (With mingw-w64, 26*68d75effSDimitry Andric // there's a risk of windows.h pulling in stdint.h.) 27*68d75effSDimitry Andric typedef int BOOL; 28*68d75effSDimitry Andric typedef void *HANDLE; 29*68d75effSDimitry Andric typedef const void *LPCVOID; 30*68d75effSDimitry Andric typedef void *LPVOID; 31*68d75effSDimitry Andric 32*68d75effSDimitry Andric typedef unsigned long DWORD; 33*68d75effSDimitry Andric constexpr unsigned long HEAP_ZERO_MEMORY = 0x00000008; 34*68d75effSDimitry Andric constexpr unsigned long HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010; 35*68d75effSDimitry Andric constexpr unsigned long HEAP_ALLOCATE_SUPPORTED_FLAGS = (HEAP_ZERO_MEMORY); 36*68d75effSDimitry Andric constexpr unsigned long HEAP_ALLOCATE_UNSUPPORTED_FLAGS = 37*68d75effSDimitry Andric (~HEAP_ALLOCATE_SUPPORTED_FLAGS); 38*68d75effSDimitry Andric constexpr unsigned long HEAP_FREE_SUPPORTED_FLAGS = (0); 39*68d75effSDimitry Andric constexpr unsigned long HEAP_FREE_UNSUPPORTED_FLAGS = 40*68d75effSDimitry Andric (~HEAP_ALLOCATE_SUPPORTED_FLAGS); 41*68d75effSDimitry Andric constexpr unsigned long HEAP_REALLOC_SUPPORTED_FLAGS = 42*68d75effSDimitry Andric (HEAP_REALLOC_IN_PLACE_ONLY | HEAP_ZERO_MEMORY); 43*68d75effSDimitry Andric constexpr unsigned long HEAP_REALLOC_UNSUPPORTED_FLAGS = 44*68d75effSDimitry Andric (~HEAP_ALLOCATE_SUPPORTED_FLAGS); 45*68d75effSDimitry Andric 46*68d75effSDimitry Andric 47*68d75effSDimitry Andric extern "C" { 48*68d75effSDimitry Andric LPVOID WINAPI HeapAlloc(HANDLE hHeap, DWORD dwFlags, size_t dwBytes); 49*68d75effSDimitry Andric LPVOID WINAPI HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, 50*68d75effSDimitry Andric size_t dwBytes); 51*68d75effSDimitry Andric BOOL WINAPI HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); 52*68d75effSDimitry Andric size_t WINAPI HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); 53*68d75effSDimitry Andric 54*68d75effSDimitry Andric BOOL WINAPI HeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); 55*68d75effSDimitry Andric } 56*68d75effSDimitry Andric 57*68d75effSDimitry Andric using namespace __asan; 58*68d75effSDimitry Andric 59*68d75effSDimitry Andric // MT: Simply defining functions with the same signature in *.obj 60*68d75effSDimitry Andric // files overrides the standard functions in the CRT. 61*68d75effSDimitry Andric // MD: Memory allocation functions are defined in the CRT .dll, 62*68d75effSDimitry Andric // so we have to intercept them before they are called for the first time. 63*68d75effSDimitry Andric 64*68d75effSDimitry Andric #if ASAN_DYNAMIC 65*68d75effSDimitry Andric # define ALLOCATION_FUNCTION_ATTRIBUTE 66*68d75effSDimitry Andric #else 67*68d75effSDimitry Andric # define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE 68*68d75effSDimitry Andric #endif 69*68d75effSDimitry Andric 70*68d75effSDimitry Andric extern "C" { 71*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 72*68d75effSDimitry Andric size_t _msize(void *ptr) { 73*68d75effSDimitry Andric GET_CURRENT_PC_BP_SP; 74*68d75effSDimitry Andric (void)sp; 75*68d75effSDimitry Andric return asan_malloc_usable_size(ptr, pc, bp); 76*68d75effSDimitry Andric } 77*68d75effSDimitry Andric 78*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 79*68d75effSDimitry Andric size_t _msize_base(void *ptr) { 80*68d75effSDimitry Andric return _msize(ptr); 81*68d75effSDimitry Andric } 82*68d75effSDimitry Andric 83*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 84*68d75effSDimitry Andric void free(void *ptr) { 85*68d75effSDimitry Andric GET_STACK_TRACE_FREE; 86*68d75effSDimitry Andric return asan_free(ptr, &stack, FROM_MALLOC); 87*68d75effSDimitry Andric } 88*68d75effSDimitry Andric 89*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 90*68d75effSDimitry Andric void _free_dbg(void *ptr, int) { 91*68d75effSDimitry Andric free(ptr); 92*68d75effSDimitry Andric } 93*68d75effSDimitry Andric 94*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 95*68d75effSDimitry Andric void _free_base(void *ptr) { 96*68d75effSDimitry Andric free(ptr); 97*68d75effSDimitry Andric } 98*68d75effSDimitry Andric 99*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 100*68d75effSDimitry Andric void *malloc(size_t size) { 101*68d75effSDimitry Andric GET_STACK_TRACE_MALLOC; 102*68d75effSDimitry Andric return asan_malloc(size, &stack); 103*68d75effSDimitry Andric } 104*68d75effSDimitry Andric 105*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 106*68d75effSDimitry Andric void *_malloc_base(size_t size) { 107*68d75effSDimitry Andric return malloc(size); 108*68d75effSDimitry Andric } 109*68d75effSDimitry Andric 110*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 111*68d75effSDimitry Andric void *_malloc_dbg(size_t size, int, const char *, int) { 112*68d75effSDimitry Andric return malloc(size); 113*68d75effSDimitry Andric } 114*68d75effSDimitry Andric 115*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 116*68d75effSDimitry Andric void *calloc(size_t nmemb, size_t size) { 117*68d75effSDimitry Andric GET_STACK_TRACE_MALLOC; 118*68d75effSDimitry Andric return asan_calloc(nmemb, size, &stack); 119*68d75effSDimitry Andric } 120*68d75effSDimitry Andric 121*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 122*68d75effSDimitry Andric void *_calloc_base(size_t nmemb, size_t size) { 123*68d75effSDimitry Andric return calloc(nmemb, size); 124*68d75effSDimitry Andric } 125*68d75effSDimitry Andric 126*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 127*68d75effSDimitry Andric void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) { 128*68d75effSDimitry Andric return calloc(nmemb, size); 129*68d75effSDimitry Andric } 130*68d75effSDimitry Andric 131*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 132*68d75effSDimitry Andric void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { 133*68d75effSDimitry Andric return calloc(nmemb, size); 134*68d75effSDimitry Andric } 135*68d75effSDimitry Andric 136*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 137*68d75effSDimitry Andric void *realloc(void *ptr, size_t size) { 138*68d75effSDimitry Andric GET_STACK_TRACE_MALLOC; 139*68d75effSDimitry Andric return asan_realloc(ptr, size, &stack); 140*68d75effSDimitry Andric } 141*68d75effSDimitry Andric 142*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 143*68d75effSDimitry Andric void *_realloc_dbg(void *ptr, size_t size, int) { 144*68d75effSDimitry Andric UNREACHABLE("_realloc_dbg should not exist!"); 145*68d75effSDimitry Andric return 0; 146*68d75effSDimitry Andric } 147*68d75effSDimitry Andric 148*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 149*68d75effSDimitry Andric void *_realloc_base(void *ptr, size_t size) { 150*68d75effSDimitry Andric return realloc(ptr, size); 151*68d75effSDimitry Andric } 152*68d75effSDimitry Andric 153*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 154*68d75effSDimitry Andric void *_recalloc(void *p, size_t n, size_t elem_size) { 155*68d75effSDimitry Andric if (!p) 156*68d75effSDimitry Andric return calloc(n, elem_size); 157*68d75effSDimitry Andric const size_t size = n * elem_size; 158*68d75effSDimitry Andric if (elem_size != 0 && size / elem_size != n) 159*68d75effSDimitry Andric return 0; 160*68d75effSDimitry Andric 161*68d75effSDimitry Andric size_t old_size = _msize(p); 162*68d75effSDimitry Andric void *new_alloc = malloc(size); 163*68d75effSDimitry Andric if (new_alloc) { 164*68d75effSDimitry Andric REAL(memcpy)(new_alloc, p, Min<size_t>(size, old_size)); 165*68d75effSDimitry Andric if (old_size < size) 166*68d75effSDimitry Andric REAL(memset)(((u8 *)new_alloc) + old_size, 0, size - old_size); 167*68d75effSDimitry Andric free(p); 168*68d75effSDimitry Andric } 169*68d75effSDimitry Andric return new_alloc; 170*68d75effSDimitry Andric } 171*68d75effSDimitry Andric 172*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 173*68d75effSDimitry Andric void *_recalloc_base(void *p, size_t n, size_t elem_size) { 174*68d75effSDimitry Andric return _recalloc(p, n, elem_size); 175*68d75effSDimitry Andric } 176*68d75effSDimitry Andric 177*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 178*68d75effSDimitry Andric void *_expand(void *memblock, size_t size) { 179*68d75effSDimitry Andric // _expand is used in realloc-like functions to resize the buffer if possible. 180*68d75effSDimitry Andric // We don't want memory to stand still while resizing buffers, so return 0. 181*68d75effSDimitry Andric return 0; 182*68d75effSDimitry Andric } 183*68d75effSDimitry Andric 184*68d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 185*68d75effSDimitry Andric void *_expand_dbg(void *memblock, size_t size) { 186*68d75effSDimitry Andric return _expand(memblock, size); 187*68d75effSDimitry Andric } 188*68d75effSDimitry Andric 189*68d75effSDimitry Andric // TODO(timurrrr): Might want to add support for _aligned_* allocation 190*68d75effSDimitry Andric // functions to detect a bit more bugs. Those functions seem to wrap malloc(). 191*68d75effSDimitry Andric 192*68d75effSDimitry Andric int _CrtDbgReport(int, const char*, int, 193*68d75effSDimitry Andric const char*, const char*, ...) { 194*68d75effSDimitry Andric ShowStatsAndAbort(); 195*68d75effSDimitry Andric } 196*68d75effSDimitry Andric 197*68d75effSDimitry Andric int _CrtDbgReportW(int reportType, const wchar_t*, int, 198*68d75effSDimitry Andric const wchar_t*, const wchar_t*, ...) { 199*68d75effSDimitry Andric ShowStatsAndAbort(); 200*68d75effSDimitry Andric } 201*68d75effSDimitry Andric 202*68d75effSDimitry Andric int _CrtSetReportMode(int, int) { 203*68d75effSDimitry Andric return 0; 204*68d75effSDimitry Andric } 205*68d75effSDimitry Andric } // extern "C" 206*68d75effSDimitry Andric 207*68d75effSDimitry Andric #define OWNED_BY_RTL(heap, memory) \ 208*68d75effSDimitry Andric (!__sanitizer_get_ownership(memory) && HeapValidate(heap, 0, memory)) 209*68d75effSDimitry Andric 210*68d75effSDimitry Andric INTERCEPTOR_WINAPI(size_t, HeapSize, HANDLE hHeap, DWORD dwFlags, 211*68d75effSDimitry Andric LPCVOID lpMem) { 212*68d75effSDimitry Andric // If the RTL allocators are hooked we need to check whether the ASAN 213*68d75effSDimitry Andric // allocator owns the pointer we're about to use. Allocations occur before 214*68d75effSDimitry Andric // interception takes place, so if it is not owned by the RTL heap we can 215*68d75effSDimitry Andric // pass it to the ASAN heap for inspection. 216*68d75effSDimitry Andric if (flags()->windows_hook_rtl_allocators) { 217*68d75effSDimitry Andric if (!asan_inited || OWNED_BY_RTL(hHeap, lpMem)) 218*68d75effSDimitry Andric return REAL(HeapSize)(hHeap, dwFlags, lpMem); 219*68d75effSDimitry Andric } else { 220*68d75effSDimitry Andric CHECK(dwFlags == 0 && "unsupported heap flags"); 221*68d75effSDimitry Andric } 222*68d75effSDimitry Andric GET_CURRENT_PC_BP_SP; 223*68d75effSDimitry Andric (void)sp; 224*68d75effSDimitry Andric return asan_malloc_usable_size(lpMem, pc, bp); 225*68d75effSDimitry Andric } 226*68d75effSDimitry Andric 227*68d75effSDimitry Andric INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags, 228*68d75effSDimitry Andric size_t dwBytes) { 229*68d75effSDimitry Andric // If the ASAN runtime is not initialized, or we encounter an unsupported 230*68d75effSDimitry Andric // flag, fall back to the original allocator. 231*68d75effSDimitry Andric if (flags()->windows_hook_rtl_allocators) { 232*68d75effSDimitry Andric if (UNLIKELY(!asan_inited || 233*68d75effSDimitry Andric (dwFlags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) { 234*68d75effSDimitry Andric return REAL(HeapAlloc)(hHeap, dwFlags, dwBytes); 235*68d75effSDimitry Andric } 236*68d75effSDimitry Andric } else { 237*68d75effSDimitry Andric // In the case that we don't hook the rtl allocators, 238*68d75effSDimitry Andric // this becomes an assert since there is no failover to the original 239*68d75effSDimitry Andric // allocator. 240*68d75effSDimitry Andric CHECK((HEAP_ALLOCATE_UNSUPPORTED_FLAGS & dwFlags) != 0 && 241*68d75effSDimitry Andric "unsupported flags"); 242*68d75effSDimitry Andric } 243*68d75effSDimitry Andric GET_STACK_TRACE_MALLOC; 244*68d75effSDimitry Andric void *p = asan_malloc(dwBytes, &stack); 245*68d75effSDimitry Andric // Reading MSDN suggests that the *entire* usable allocation is zeroed out. 246*68d75effSDimitry Andric // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. 247*68d75effSDimitry Andric // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 248*68d75effSDimitry Andric if (p && (dwFlags & HEAP_ZERO_MEMORY)) { 249*68d75effSDimitry Andric GET_CURRENT_PC_BP_SP; 250*68d75effSDimitry Andric (void)sp; 251*68d75effSDimitry Andric auto usable_size = asan_malloc_usable_size(p, pc, bp); 252*68d75effSDimitry Andric internal_memset(p, 0, usable_size); 253*68d75effSDimitry Andric } 254*68d75effSDimitry Andric return p; 255*68d75effSDimitry Andric } 256*68d75effSDimitry Andric 257*68d75effSDimitry Andric INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) { 258*68d75effSDimitry Andric // Heap allocations happen before this function is hooked, so we must fall 259*68d75effSDimitry Andric // back to the original function if the pointer is not from the ASAN heap, 260*68d75effSDimitry Andric // or unsupported flags are provided. 261*68d75effSDimitry Andric if (flags()->windows_hook_rtl_allocators) { 262*68d75effSDimitry Andric if (OWNED_BY_RTL(hHeap, lpMem)) 263*68d75effSDimitry Andric return REAL(HeapFree)(hHeap, dwFlags, lpMem); 264*68d75effSDimitry Andric } else { 265*68d75effSDimitry Andric CHECK((HEAP_FREE_UNSUPPORTED_FLAGS & dwFlags) != 0 && "unsupported flags"); 266*68d75effSDimitry Andric } 267*68d75effSDimitry Andric GET_STACK_TRACE_FREE; 268*68d75effSDimitry Andric asan_free(lpMem, &stack, FROM_MALLOC); 269*68d75effSDimitry Andric return true; 270*68d75effSDimitry Andric } 271*68d75effSDimitry Andric 272*68d75effSDimitry Andric namespace __asan { 273*68d75effSDimitry Andric using AllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, size_t); 274*68d75effSDimitry Andric using ReAllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, LPVOID, size_t); 275*68d75effSDimitry Andric using SizeFunction = size_t(WINAPI *)(HANDLE, DWORD, LPVOID); 276*68d75effSDimitry Andric using FreeFunction = BOOL(WINAPI *)(HANDLE, DWORD, LPVOID); 277*68d75effSDimitry Andric 278*68d75effSDimitry Andric void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc, 279*68d75effSDimitry Andric FreeFunction freeFunc, AllocFunction allocFunc, 280*68d75effSDimitry Andric HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, size_t dwBytes) { 281*68d75effSDimitry Andric CHECK(reallocFunc && heapSizeFunc && freeFunc && allocFunc); 282*68d75effSDimitry Andric GET_STACK_TRACE_MALLOC; 283*68d75effSDimitry Andric GET_CURRENT_PC_BP_SP; 284*68d75effSDimitry Andric (void)sp; 285*68d75effSDimitry Andric if (flags()->windows_hook_rtl_allocators) { 286*68d75effSDimitry Andric enum AllocationOwnership { NEITHER = 0, ASAN = 1, RTL = 2 }; 287*68d75effSDimitry Andric AllocationOwnership ownershipState; 288*68d75effSDimitry Andric bool owned_rtlalloc = false; 289*68d75effSDimitry Andric bool owned_asan = __sanitizer_get_ownership(lpMem); 290*68d75effSDimitry Andric 291*68d75effSDimitry Andric if (!owned_asan) 292*68d75effSDimitry Andric owned_rtlalloc = HeapValidate(hHeap, 0, lpMem); 293*68d75effSDimitry Andric 294*68d75effSDimitry Andric if (owned_asan && !owned_rtlalloc) 295*68d75effSDimitry Andric ownershipState = ASAN; 296*68d75effSDimitry Andric else if (!owned_asan && owned_rtlalloc) 297*68d75effSDimitry Andric ownershipState = RTL; 298*68d75effSDimitry Andric else if (!owned_asan && !owned_rtlalloc) 299*68d75effSDimitry Andric ownershipState = NEITHER; 300*68d75effSDimitry Andric 301*68d75effSDimitry Andric // If this heap block which was allocated before the ASAN 302*68d75effSDimitry Andric // runtime came up, use the real HeapFree function. 303*68d75effSDimitry Andric if (UNLIKELY(!asan_inited)) { 304*68d75effSDimitry Andric return reallocFunc(hHeap, dwFlags, lpMem, dwBytes); 305*68d75effSDimitry Andric } 306*68d75effSDimitry Andric bool only_asan_supported_flags = 307*68d75effSDimitry Andric (HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) == 0; 308*68d75effSDimitry Andric 309*68d75effSDimitry Andric if (ownershipState == RTL || 310*68d75effSDimitry Andric (ownershipState == NEITHER && !only_asan_supported_flags)) { 311*68d75effSDimitry Andric if (only_asan_supported_flags) { 312*68d75effSDimitry Andric // if this is a conversion to ASAN upported flags, transfer this 313*68d75effSDimitry Andric // allocation to the ASAN allocator 314*68d75effSDimitry Andric void *replacement_alloc; 315*68d75effSDimitry Andric if (dwFlags & HEAP_ZERO_MEMORY) 316*68d75effSDimitry Andric replacement_alloc = asan_calloc(1, dwBytes, &stack); 317*68d75effSDimitry Andric else 318*68d75effSDimitry Andric replacement_alloc = asan_malloc(dwBytes, &stack); 319*68d75effSDimitry Andric if (replacement_alloc) { 320*68d75effSDimitry Andric size_t old_size = heapSizeFunc(hHeap, dwFlags, lpMem); 321*68d75effSDimitry Andric if (old_size == ((size_t)0) - 1) { 322*68d75effSDimitry Andric asan_free(replacement_alloc, &stack, FROM_MALLOC); 323*68d75effSDimitry Andric return nullptr; 324*68d75effSDimitry Andric } 325*68d75effSDimitry Andric REAL(memcpy)(replacement_alloc, lpMem, old_size); 326*68d75effSDimitry Andric freeFunc(hHeap, dwFlags, lpMem); 327*68d75effSDimitry Andric } 328*68d75effSDimitry Andric return replacement_alloc; 329*68d75effSDimitry Andric } else { 330*68d75effSDimitry Andric // owned by rtl or neither with unsupported ASAN flags, 331*68d75effSDimitry Andric // just pass back to original allocator 332*68d75effSDimitry Andric CHECK(ownershipState == RTL || ownershipState == NEITHER); 333*68d75effSDimitry Andric CHECK(!only_asan_supported_flags); 334*68d75effSDimitry Andric return reallocFunc(hHeap, dwFlags, lpMem, dwBytes); 335*68d75effSDimitry Andric } 336*68d75effSDimitry Andric } 337*68d75effSDimitry Andric 338*68d75effSDimitry Andric if (ownershipState == ASAN && !only_asan_supported_flags) { 339*68d75effSDimitry Andric // Conversion to unsupported flags allocation, 340*68d75effSDimitry Andric // transfer this allocation back to the original allocator. 341*68d75effSDimitry Andric void *replacement_alloc = allocFunc(hHeap, dwFlags, dwBytes); 342*68d75effSDimitry Andric size_t old_usable_size = 0; 343*68d75effSDimitry Andric if (replacement_alloc) { 344*68d75effSDimitry Andric old_usable_size = asan_malloc_usable_size(lpMem, pc, bp); 345*68d75effSDimitry Andric REAL(memcpy)(replacement_alloc, lpMem, 346*68d75effSDimitry Andric Min<size_t>(dwBytes, old_usable_size)); 347*68d75effSDimitry Andric asan_free(lpMem, &stack, FROM_MALLOC); 348*68d75effSDimitry Andric } 349*68d75effSDimitry Andric return replacement_alloc; 350*68d75effSDimitry Andric } 351*68d75effSDimitry Andric 352*68d75effSDimitry Andric CHECK((ownershipState == ASAN || ownershipState == NEITHER) && 353*68d75effSDimitry Andric only_asan_supported_flags); 354*68d75effSDimitry Andric // At this point we should either be ASAN owned with ASAN supported flags 355*68d75effSDimitry Andric // or we owned by neither and have supported flags. 356*68d75effSDimitry Andric // Pass through even when it's neither since this could be a null realloc or 357*68d75effSDimitry Andric // UAF that ASAN needs to catch. 358*68d75effSDimitry Andric } else { 359*68d75effSDimitry Andric CHECK((HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) != 0 && 360*68d75effSDimitry Andric "unsupported flags"); 361*68d75effSDimitry Andric } 362*68d75effSDimitry Andric // asan_realloc will never reallocate in place, so for now this flag is 363*68d75effSDimitry Andric // unsupported until we figure out a way to fake this. 364*68d75effSDimitry Andric if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) 365*68d75effSDimitry Andric return nullptr; 366*68d75effSDimitry Andric 367*68d75effSDimitry Andric // HeapReAlloc and HeapAlloc both happily accept 0 sized allocations. 368*68d75effSDimitry Andric // passing a 0 size into asan_realloc will free the allocation. 369*68d75effSDimitry Andric // To avoid this and keep behavior consistent, fudge the size if 0. 370*68d75effSDimitry Andric // (asan_malloc already does this) 371*68d75effSDimitry Andric if (dwBytes == 0) 372*68d75effSDimitry Andric dwBytes = 1; 373*68d75effSDimitry Andric 374*68d75effSDimitry Andric size_t old_size; 375*68d75effSDimitry Andric if (dwFlags & HEAP_ZERO_MEMORY) 376*68d75effSDimitry Andric old_size = asan_malloc_usable_size(lpMem, pc, bp); 377*68d75effSDimitry Andric 378*68d75effSDimitry Andric void *ptr = asan_realloc(lpMem, dwBytes, &stack); 379*68d75effSDimitry Andric if (ptr == nullptr) 380*68d75effSDimitry Andric return nullptr; 381*68d75effSDimitry Andric 382*68d75effSDimitry Andric if (dwFlags & HEAP_ZERO_MEMORY) { 383*68d75effSDimitry Andric size_t new_size = asan_malloc_usable_size(ptr, pc, bp); 384*68d75effSDimitry Andric if (old_size < new_size) 385*68d75effSDimitry Andric REAL(memset)(((u8 *)ptr) + old_size, 0, new_size - old_size); 386*68d75effSDimitry Andric } 387*68d75effSDimitry Andric 388*68d75effSDimitry Andric return ptr; 389*68d75effSDimitry Andric } 390*68d75effSDimitry Andric } // namespace __asan 391*68d75effSDimitry Andric 392*68d75effSDimitry Andric INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags, 393*68d75effSDimitry Andric LPVOID lpMem, size_t dwBytes) { 394*68d75effSDimitry Andric return SharedReAlloc(REAL(HeapReAlloc), (SizeFunction)REAL(HeapSize), 395*68d75effSDimitry Andric REAL(HeapFree), REAL(HeapAlloc), hHeap, dwFlags, lpMem, 396*68d75effSDimitry Andric dwBytes); 397*68d75effSDimitry Andric } 398*68d75effSDimitry Andric 399*68d75effSDimitry Andric // The following functions are undocumented and subject to change. 400*68d75effSDimitry Andric // However, hooking them is necessary to hook Windows heap 401*68d75effSDimitry Andric // allocations with detours and their definitions are unlikely to change. 402*68d75effSDimitry Andric // Comments in /minkernel/ntos/rtl/heappublic.c indicate that these functions 403*68d75effSDimitry Andric // are part of the heap's public interface. 404*68d75effSDimitry Andric typedef unsigned long LOGICAL; 405*68d75effSDimitry Andric 406*68d75effSDimitry Andric // This function is documented as part of the Driver Development Kit but *not* 407*68d75effSDimitry Andric // the Windows Development Kit. 408*68d75effSDimitry Andric LOGICAL RtlFreeHeap(void* HeapHandle, DWORD Flags, 409*68d75effSDimitry Andric void* BaseAddress); 410*68d75effSDimitry Andric 411*68d75effSDimitry Andric // This function is documented as part of the Driver Development Kit but *not* 412*68d75effSDimitry Andric // the Windows Development Kit. 413*68d75effSDimitry Andric void* RtlAllocateHeap(void* HeapHandle, DWORD Flags, size_t Size); 414*68d75effSDimitry Andric 415*68d75effSDimitry Andric // This function is completely undocumented. 416*68d75effSDimitry Andric void* 417*68d75effSDimitry Andric RtlReAllocateHeap(void* HeapHandle, DWORD Flags, void* BaseAddress, 418*68d75effSDimitry Andric size_t Size); 419*68d75effSDimitry Andric 420*68d75effSDimitry Andric // This function is completely undocumented. 421*68d75effSDimitry Andric size_t RtlSizeHeap(void* HeapHandle, DWORD Flags, void* BaseAddress); 422*68d75effSDimitry Andric 423*68d75effSDimitry Andric INTERCEPTOR_WINAPI(size_t, RtlSizeHeap, HANDLE HeapHandle, DWORD Flags, 424*68d75effSDimitry Andric void* BaseAddress) { 425*68d75effSDimitry Andric if (!flags()->windows_hook_rtl_allocators || 426*68d75effSDimitry Andric UNLIKELY(!asan_inited || OWNED_BY_RTL(HeapHandle, BaseAddress))) { 427*68d75effSDimitry Andric return REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress); 428*68d75effSDimitry Andric } 429*68d75effSDimitry Andric GET_CURRENT_PC_BP_SP; 430*68d75effSDimitry Andric (void)sp; 431*68d75effSDimitry Andric return asan_malloc_usable_size(BaseAddress, pc, bp); 432*68d75effSDimitry Andric } 433*68d75effSDimitry Andric 434*68d75effSDimitry Andric INTERCEPTOR_WINAPI(BOOL, RtlFreeHeap, HANDLE HeapHandle, DWORD Flags, 435*68d75effSDimitry Andric void* BaseAddress) { 436*68d75effSDimitry Andric // Heap allocations happen before this function is hooked, so we must fall 437*68d75effSDimitry Andric // back to the original function if the pointer is not from the ASAN heap, or 438*68d75effSDimitry Andric // unsupported flags are provided. 439*68d75effSDimitry Andric if (!flags()->windows_hook_rtl_allocators || 440*68d75effSDimitry Andric UNLIKELY((HEAP_FREE_UNSUPPORTED_FLAGS & Flags) != 0 || 441*68d75effSDimitry Andric OWNED_BY_RTL(HeapHandle, BaseAddress))) { 442*68d75effSDimitry Andric return REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress); 443*68d75effSDimitry Andric } 444*68d75effSDimitry Andric GET_STACK_TRACE_FREE; 445*68d75effSDimitry Andric asan_free(BaseAddress, &stack, FROM_MALLOC); 446*68d75effSDimitry Andric return true; 447*68d75effSDimitry Andric } 448*68d75effSDimitry Andric 449*68d75effSDimitry Andric INTERCEPTOR_WINAPI(void*, RtlAllocateHeap, HANDLE HeapHandle, DWORD Flags, 450*68d75effSDimitry Andric size_t Size) { 451*68d75effSDimitry Andric // If the ASAN runtime is not initialized, or we encounter an unsupported 452*68d75effSDimitry Andric // flag, fall back to the original allocator. 453*68d75effSDimitry Andric if (!flags()->windows_hook_rtl_allocators || 454*68d75effSDimitry Andric UNLIKELY(!asan_inited || 455*68d75effSDimitry Andric (Flags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) { 456*68d75effSDimitry Andric return REAL(RtlAllocateHeap)(HeapHandle, Flags, Size); 457*68d75effSDimitry Andric } 458*68d75effSDimitry Andric GET_STACK_TRACE_MALLOC; 459*68d75effSDimitry Andric void *p; 460*68d75effSDimitry Andric // Reading MSDN suggests that the *entire* usable allocation is zeroed out. 461*68d75effSDimitry Andric // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. 462*68d75effSDimitry Andric // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 463*68d75effSDimitry Andric if (Flags & HEAP_ZERO_MEMORY) { 464*68d75effSDimitry Andric p = asan_calloc(Size, 1, &stack); 465*68d75effSDimitry Andric } else { 466*68d75effSDimitry Andric p = asan_malloc(Size, &stack); 467*68d75effSDimitry Andric } 468*68d75effSDimitry Andric return p; 469*68d75effSDimitry Andric } 470*68d75effSDimitry Andric 471*68d75effSDimitry Andric INTERCEPTOR_WINAPI(void*, RtlReAllocateHeap, HANDLE HeapHandle, DWORD Flags, 472*68d75effSDimitry Andric void* BaseAddress, size_t Size) { 473*68d75effSDimitry Andric // If it's actually a heap block which was allocated before the ASAN runtime 474*68d75effSDimitry Andric // came up, use the real RtlFreeHeap function. 475*68d75effSDimitry Andric if (!flags()->windows_hook_rtl_allocators) 476*68d75effSDimitry Andric return REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size); 477*68d75effSDimitry Andric 478*68d75effSDimitry Andric return SharedReAlloc(REAL(RtlReAllocateHeap), REAL(RtlSizeHeap), 479*68d75effSDimitry Andric REAL(RtlFreeHeap), REAL(RtlAllocateHeap), HeapHandle, 480*68d75effSDimitry Andric Flags, BaseAddress, Size); 481*68d75effSDimitry Andric } 482*68d75effSDimitry Andric 483*68d75effSDimitry Andric namespace __asan { 484*68d75effSDimitry Andric 485*68d75effSDimitry Andric static void TryToOverrideFunction(const char *fname, uptr new_func) { 486*68d75effSDimitry Andric // Failure here is not fatal. The CRT may not be present, and different CRT 487*68d75effSDimitry Andric // versions use different symbols. 488*68d75effSDimitry Andric if (!__interception::OverrideFunction(fname, new_func)) 489*68d75effSDimitry Andric VPrintf(2, "Failed to override function %s\n", fname); 490*68d75effSDimitry Andric } 491*68d75effSDimitry Andric 492*68d75effSDimitry Andric void ReplaceSystemMalloc() { 493*68d75effSDimitry Andric #if defined(ASAN_DYNAMIC) 494*68d75effSDimitry Andric TryToOverrideFunction("free", (uptr)free); 495*68d75effSDimitry Andric TryToOverrideFunction("_free_base", (uptr)free); 496*68d75effSDimitry Andric TryToOverrideFunction("malloc", (uptr)malloc); 497*68d75effSDimitry Andric TryToOverrideFunction("_malloc_base", (uptr)malloc); 498*68d75effSDimitry Andric TryToOverrideFunction("_malloc_crt", (uptr)malloc); 499*68d75effSDimitry Andric TryToOverrideFunction("calloc", (uptr)calloc); 500*68d75effSDimitry Andric TryToOverrideFunction("_calloc_base", (uptr)calloc); 501*68d75effSDimitry Andric TryToOverrideFunction("_calloc_crt", (uptr)calloc); 502*68d75effSDimitry Andric TryToOverrideFunction("realloc", (uptr)realloc); 503*68d75effSDimitry Andric TryToOverrideFunction("_realloc_base", (uptr)realloc); 504*68d75effSDimitry Andric TryToOverrideFunction("_realloc_crt", (uptr)realloc); 505*68d75effSDimitry Andric TryToOverrideFunction("_recalloc", (uptr)_recalloc); 506*68d75effSDimitry Andric TryToOverrideFunction("_recalloc_base", (uptr)_recalloc); 507*68d75effSDimitry Andric TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc); 508*68d75effSDimitry Andric TryToOverrideFunction("_msize", (uptr)_msize); 509*68d75effSDimitry Andric TryToOverrideFunction("_msize_base", (uptr)_msize); 510*68d75effSDimitry Andric TryToOverrideFunction("_expand", (uptr)_expand); 511*68d75effSDimitry Andric TryToOverrideFunction("_expand_base", (uptr)_expand); 512*68d75effSDimitry Andric 513*68d75effSDimitry Andric if (flags()->windows_hook_rtl_allocators) { 514*68d75effSDimitry Andric INTERCEPT_FUNCTION(HeapSize); 515*68d75effSDimitry Andric INTERCEPT_FUNCTION(HeapFree); 516*68d75effSDimitry Andric INTERCEPT_FUNCTION(HeapReAlloc); 517*68d75effSDimitry Andric INTERCEPT_FUNCTION(HeapAlloc); 518*68d75effSDimitry Andric 519*68d75effSDimitry Andric // Undocumented functions must be intercepted by name, not by symbol. 520*68d75effSDimitry Andric __interception::OverrideFunction("RtlSizeHeap", (uptr)WRAP(RtlSizeHeap), 521*68d75effSDimitry Andric (uptr *)&REAL(RtlSizeHeap)); 522*68d75effSDimitry Andric __interception::OverrideFunction("RtlFreeHeap", (uptr)WRAP(RtlFreeHeap), 523*68d75effSDimitry Andric (uptr *)&REAL(RtlFreeHeap)); 524*68d75effSDimitry Andric __interception::OverrideFunction("RtlReAllocateHeap", 525*68d75effSDimitry Andric (uptr)WRAP(RtlReAllocateHeap), 526*68d75effSDimitry Andric (uptr *)&REAL(RtlReAllocateHeap)); 527*68d75effSDimitry Andric __interception::OverrideFunction("RtlAllocateHeap", 528*68d75effSDimitry Andric (uptr)WRAP(RtlAllocateHeap), 529*68d75effSDimitry Andric (uptr *)&REAL(RtlAllocateHeap)); 530*68d75effSDimitry Andric } else { 531*68d75effSDimitry Andric #define INTERCEPT_UCRT_FUNCTION(func) \ 532*68d75effSDimitry Andric if (!INTERCEPT_FUNCTION_DLLIMPORT( \ 533*68d75effSDimitry Andric "ucrtbase.dll", "api-ms-win-core-heap-l1-1-0.dll", func)) { \ 534*68d75effSDimitry Andric VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func); \ 535*68d75effSDimitry Andric } 536*68d75effSDimitry Andric INTERCEPT_UCRT_FUNCTION(HeapAlloc); 537*68d75effSDimitry Andric INTERCEPT_UCRT_FUNCTION(HeapFree); 538*68d75effSDimitry Andric INTERCEPT_UCRT_FUNCTION(HeapReAlloc); 539*68d75effSDimitry Andric INTERCEPT_UCRT_FUNCTION(HeapSize); 540*68d75effSDimitry Andric #undef INTERCEPT_UCRT_FUNCTION 541*68d75effSDimitry Andric } 542*68d75effSDimitry Andric // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which 543*68d75effSDimitry Andric // enable cross-module inlining. This means our _malloc_base hook won't catch 544*68d75effSDimitry Andric // all CRT allocations. This code here patches the import table of 545*68d75effSDimitry Andric // ucrtbase.dll so that all attempts to use the lower-level win32 heap 546*68d75effSDimitry Andric // allocation API will be directed to ASan's heap. We don't currently 547*68d75effSDimitry Andric // intercept all calls to HeapAlloc. If we did, we would have to check on 548*68d75effSDimitry Andric // HeapFree whether the pointer came from ASan of from the system. 549*68d75effSDimitry Andric 550*68d75effSDimitry Andric #endif // defined(ASAN_DYNAMIC) 551*68d75effSDimitry Andric } 552*68d75effSDimitry Andric } // namespace __asan 553*68d75effSDimitry Andric 554*68d75effSDimitry Andric #endif // _WIN32 555