1353944Sdim//===-- asan_malloc_win.cpp -----------------------------------------------===// 2353944Sdim// 3353944Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353944Sdim// See https://llvm.org/LICENSE.txt for license information. 5353944Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6353944Sdim// 7353944Sdim//===----------------------------------------------------------------------===// 8353944Sdim// 9353944Sdim// This file is a part of AddressSanitizer, an address sanity checker. 10353944Sdim// 11353944Sdim// Windows-specific malloc interception. 12353944Sdim//===----------------------------------------------------------------------===// 13353944Sdim 14353944Sdim#include "sanitizer_common/sanitizer_allocator_interface.h" 15353944Sdim#include "sanitizer_common/sanitizer_platform.h" 16353944Sdim#if SANITIZER_WINDOWS 17353944Sdim#include "asan_allocator.h" 18353944Sdim#include "asan_interceptors.h" 19353944Sdim#include "asan_internal.h" 20353944Sdim#include "asan_stack.h" 21353944Sdim#include "interception/interception.h" 22353944Sdim#include <stddef.h> 23353944Sdim 24353944Sdim// Intentionally not including windows.h here, to avoid the risk of 25353944Sdim// pulling in conflicting declarations of these functions. (With mingw-w64, 26353944Sdim// there's a risk of windows.h pulling in stdint.h.) 27353944Sdimtypedef int BOOL; 28353944Sdimtypedef void *HANDLE; 29353944Sdimtypedef const void *LPCVOID; 30353944Sdimtypedef void *LPVOID; 31353944Sdim 32353944Sdimtypedef unsigned long DWORD; 33353944Sdimconstexpr unsigned long HEAP_ZERO_MEMORY = 0x00000008; 34353944Sdimconstexpr unsigned long HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010; 35353944Sdimconstexpr unsigned long HEAP_ALLOCATE_SUPPORTED_FLAGS = (HEAP_ZERO_MEMORY); 36353944Sdimconstexpr unsigned long HEAP_ALLOCATE_UNSUPPORTED_FLAGS = 37353944Sdim (~HEAP_ALLOCATE_SUPPORTED_FLAGS); 38353944Sdimconstexpr unsigned long HEAP_FREE_UNSUPPORTED_FLAGS = 39353944Sdim (~HEAP_ALLOCATE_SUPPORTED_FLAGS); 40353944Sdimconstexpr unsigned long HEAP_REALLOC_UNSUPPORTED_FLAGS = 41353944Sdim (~HEAP_ALLOCATE_SUPPORTED_FLAGS); 42353944Sdim 43353944Sdim 44353944Sdimextern "C" { 45353944SdimLPVOID WINAPI HeapAlloc(HANDLE hHeap, DWORD dwFlags, size_t dwBytes); 46353944SdimLPVOID WINAPI HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, 47353944Sdim size_t dwBytes); 48353944SdimBOOL WINAPI HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); 49353944Sdimsize_t WINAPI HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); 50353944Sdim 51353944SdimBOOL WINAPI HeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); 52353944Sdim} 53353944Sdim 54353944Sdimusing namespace __asan; 55353944Sdim 56353944Sdim// MT: Simply defining functions with the same signature in *.obj 57353944Sdim// files overrides the standard functions in the CRT. 58353944Sdim// MD: Memory allocation functions are defined in the CRT .dll, 59353944Sdim// so we have to intercept them before they are called for the first time. 60353944Sdim 61353944Sdim#if ASAN_DYNAMIC 62353944Sdim# define ALLOCATION_FUNCTION_ATTRIBUTE 63353944Sdim#else 64353944Sdim# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE 65353944Sdim#endif 66353944Sdim 67353944Sdimextern "C" { 68353944SdimALLOCATION_FUNCTION_ATTRIBUTE 69353944Sdimsize_t _msize(void *ptr) { 70353944Sdim GET_CURRENT_PC_BP_SP; 71353944Sdim (void)sp; 72353944Sdim return asan_malloc_usable_size(ptr, pc, bp); 73353944Sdim} 74353944Sdim 75353944SdimALLOCATION_FUNCTION_ATTRIBUTE 76353944Sdimsize_t _msize_base(void *ptr) { 77353944Sdim return _msize(ptr); 78353944Sdim} 79353944Sdim 80353944SdimALLOCATION_FUNCTION_ATTRIBUTE 81353944Sdimvoid free(void *ptr) { 82353944Sdim GET_STACK_TRACE_FREE; 83353944Sdim return asan_free(ptr, &stack, FROM_MALLOC); 84353944Sdim} 85353944Sdim 86353944SdimALLOCATION_FUNCTION_ATTRIBUTE 87353944Sdimvoid _free_dbg(void *ptr, int) { 88353944Sdim free(ptr); 89353944Sdim} 90353944Sdim 91353944SdimALLOCATION_FUNCTION_ATTRIBUTE 92353944Sdimvoid _free_base(void *ptr) { 93353944Sdim free(ptr); 94353944Sdim} 95353944Sdim 96353944SdimALLOCATION_FUNCTION_ATTRIBUTE 97353944Sdimvoid *malloc(size_t size) { 98353944Sdim GET_STACK_TRACE_MALLOC; 99353944Sdim return asan_malloc(size, &stack); 100353944Sdim} 101353944Sdim 102353944SdimALLOCATION_FUNCTION_ATTRIBUTE 103353944Sdimvoid *_malloc_base(size_t size) { 104353944Sdim return malloc(size); 105353944Sdim} 106353944Sdim 107353944SdimALLOCATION_FUNCTION_ATTRIBUTE 108353944Sdimvoid *_malloc_dbg(size_t size, int, const char *, int) { 109353944Sdim return malloc(size); 110353944Sdim} 111353944Sdim 112353944SdimALLOCATION_FUNCTION_ATTRIBUTE 113353944Sdimvoid *calloc(size_t nmemb, size_t size) { 114353944Sdim GET_STACK_TRACE_MALLOC; 115353944Sdim return asan_calloc(nmemb, size, &stack); 116353944Sdim} 117353944Sdim 118353944SdimALLOCATION_FUNCTION_ATTRIBUTE 119353944Sdimvoid *_calloc_base(size_t nmemb, size_t size) { 120353944Sdim return calloc(nmemb, size); 121353944Sdim} 122353944Sdim 123353944SdimALLOCATION_FUNCTION_ATTRIBUTE 124353944Sdimvoid *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) { 125353944Sdim return calloc(nmemb, size); 126353944Sdim} 127353944Sdim 128353944SdimALLOCATION_FUNCTION_ATTRIBUTE 129353944Sdimvoid *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { 130353944Sdim return calloc(nmemb, size); 131353944Sdim} 132353944Sdim 133353944SdimALLOCATION_FUNCTION_ATTRIBUTE 134353944Sdimvoid *realloc(void *ptr, size_t size) { 135353944Sdim GET_STACK_TRACE_MALLOC; 136353944Sdim return asan_realloc(ptr, size, &stack); 137353944Sdim} 138353944Sdim 139353944SdimALLOCATION_FUNCTION_ATTRIBUTE 140353944Sdimvoid *_realloc_dbg(void *ptr, size_t size, int) { 141353944Sdim UNREACHABLE("_realloc_dbg should not exist!"); 142353944Sdim return 0; 143353944Sdim} 144353944Sdim 145353944SdimALLOCATION_FUNCTION_ATTRIBUTE 146353944Sdimvoid *_realloc_base(void *ptr, size_t size) { 147353944Sdim return realloc(ptr, size); 148353944Sdim} 149353944Sdim 150353944SdimALLOCATION_FUNCTION_ATTRIBUTE 151353944Sdimvoid *_recalloc(void *p, size_t n, size_t elem_size) { 152353944Sdim if (!p) 153353944Sdim return calloc(n, elem_size); 154353944Sdim const size_t size = n * elem_size; 155353944Sdim if (elem_size != 0 && size / elem_size != n) 156353944Sdim return 0; 157353944Sdim 158353944Sdim size_t old_size = _msize(p); 159353944Sdim void *new_alloc = malloc(size); 160353944Sdim if (new_alloc) { 161353944Sdim REAL(memcpy)(new_alloc, p, Min<size_t>(size, old_size)); 162353944Sdim if (old_size < size) 163353944Sdim REAL(memset)(((u8 *)new_alloc) + old_size, 0, size - old_size); 164353944Sdim free(p); 165353944Sdim } 166353944Sdim return new_alloc; 167353944Sdim} 168353944Sdim 169353944SdimALLOCATION_FUNCTION_ATTRIBUTE 170353944Sdimvoid *_recalloc_base(void *p, size_t n, size_t elem_size) { 171353944Sdim return _recalloc(p, n, elem_size); 172353944Sdim} 173353944Sdim 174353944SdimALLOCATION_FUNCTION_ATTRIBUTE 175353944Sdimvoid *_expand(void *memblock, size_t size) { 176353944Sdim // _expand is used in realloc-like functions to resize the buffer if possible. 177353944Sdim // We don't want memory to stand still while resizing buffers, so return 0. 178353944Sdim return 0; 179353944Sdim} 180353944Sdim 181353944SdimALLOCATION_FUNCTION_ATTRIBUTE 182353944Sdimvoid *_expand_dbg(void *memblock, size_t size) { 183353944Sdim return _expand(memblock, size); 184353944Sdim} 185353944Sdim 186353944Sdim// TODO(timurrrr): Might want to add support for _aligned_* allocation 187353944Sdim// functions to detect a bit more bugs. Those functions seem to wrap malloc(). 188353944Sdim 189353944Sdimint _CrtDbgReport(int, const char*, int, 190353944Sdim const char*, const char*, ...) { 191353944Sdim ShowStatsAndAbort(); 192353944Sdim} 193353944Sdim 194353944Sdimint _CrtDbgReportW(int reportType, const wchar_t*, int, 195353944Sdim const wchar_t*, const wchar_t*, ...) { 196353944Sdim ShowStatsAndAbort(); 197353944Sdim} 198353944Sdim 199353944Sdimint _CrtSetReportMode(int, int) { 200353944Sdim return 0; 201353944Sdim} 202353944Sdim} // extern "C" 203353944Sdim 204353944Sdim#define OWNED_BY_RTL(heap, memory) \ 205353944Sdim (!__sanitizer_get_ownership(memory) && HeapValidate(heap, 0, memory)) 206353944Sdim 207353944SdimINTERCEPTOR_WINAPI(size_t, HeapSize, HANDLE hHeap, DWORD dwFlags, 208353944Sdim LPCVOID lpMem) { 209353944Sdim // If the RTL allocators are hooked we need to check whether the ASAN 210353944Sdim // allocator owns the pointer we're about to use. Allocations occur before 211353944Sdim // interception takes place, so if it is not owned by the RTL heap we can 212353944Sdim // pass it to the ASAN heap for inspection. 213353944Sdim if (flags()->windows_hook_rtl_allocators) { 214353944Sdim if (!asan_inited || OWNED_BY_RTL(hHeap, lpMem)) 215353944Sdim return REAL(HeapSize)(hHeap, dwFlags, lpMem); 216353944Sdim } else { 217353944Sdim CHECK(dwFlags == 0 && "unsupported heap flags"); 218353944Sdim } 219353944Sdim GET_CURRENT_PC_BP_SP; 220353944Sdim (void)sp; 221353944Sdim return asan_malloc_usable_size(lpMem, pc, bp); 222353944Sdim} 223353944Sdim 224353944SdimINTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags, 225353944Sdim size_t dwBytes) { 226353944Sdim // If the ASAN runtime is not initialized, or we encounter an unsupported 227353944Sdim // flag, fall back to the original allocator. 228353944Sdim if (flags()->windows_hook_rtl_allocators) { 229353944Sdim if (UNLIKELY(!asan_inited || 230353944Sdim (dwFlags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) { 231353944Sdim return REAL(HeapAlloc)(hHeap, dwFlags, dwBytes); 232353944Sdim } 233353944Sdim } else { 234353944Sdim // In the case that we don't hook the rtl allocators, 235353944Sdim // this becomes an assert since there is no failover to the original 236353944Sdim // allocator. 237353944Sdim CHECK((HEAP_ALLOCATE_UNSUPPORTED_FLAGS & dwFlags) != 0 && 238353944Sdim "unsupported flags"); 239353944Sdim } 240353944Sdim GET_STACK_TRACE_MALLOC; 241353944Sdim void *p = asan_malloc(dwBytes, &stack); 242353944Sdim // Reading MSDN suggests that the *entire* usable allocation is zeroed out. 243353944Sdim // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. 244353944Sdim // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 245353944Sdim if (p && (dwFlags & HEAP_ZERO_MEMORY)) { 246353944Sdim GET_CURRENT_PC_BP_SP; 247353944Sdim (void)sp; 248353944Sdim auto usable_size = asan_malloc_usable_size(p, pc, bp); 249353944Sdim internal_memset(p, 0, usable_size); 250353944Sdim } 251353944Sdim return p; 252353944Sdim} 253353944Sdim 254353944SdimINTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) { 255353944Sdim // Heap allocations happen before this function is hooked, so we must fall 256353944Sdim // back to the original function if the pointer is not from the ASAN heap, 257353944Sdim // or unsupported flags are provided. 258353944Sdim if (flags()->windows_hook_rtl_allocators) { 259353944Sdim if (OWNED_BY_RTL(hHeap, lpMem)) 260353944Sdim return REAL(HeapFree)(hHeap, dwFlags, lpMem); 261353944Sdim } else { 262353944Sdim CHECK((HEAP_FREE_UNSUPPORTED_FLAGS & dwFlags) != 0 && "unsupported flags"); 263353944Sdim } 264353944Sdim GET_STACK_TRACE_FREE; 265353944Sdim asan_free(lpMem, &stack, FROM_MALLOC); 266353944Sdim return true; 267353944Sdim} 268353944Sdim 269353944Sdimnamespace __asan { 270353944Sdimusing AllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, size_t); 271353944Sdimusing ReAllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, LPVOID, size_t); 272353944Sdimusing SizeFunction = size_t(WINAPI *)(HANDLE, DWORD, LPVOID); 273353944Sdimusing FreeFunction = BOOL(WINAPI *)(HANDLE, DWORD, LPVOID); 274353944Sdim 275353944Sdimvoid *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc, 276353944Sdim FreeFunction freeFunc, AllocFunction allocFunc, 277353944Sdim HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, size_t dwBytes) { 278353944Sdim CHECK(reallocFunc && heapSizeFunc && freeFunc && allocFunc); 279353944Sdim GET_STACK_TRACE_MALLOC; 280353944Sdim GET_CURRENT_PC_BP_SP; 281353944Sdim (void)sp; 282353944Sdim if (flags()->windows_hook_rtl_allocators) { 283353944Sdim enum AllocationOwnership { NEITHER = 0, ASAN = 1, RTL = 2 }; 284353944Sdim AllocationOwnership ownershipState; 285353944Sdim bool owned_rtlalloc = false; 286353944Sdim bool owned_asan = __sanitizer_get_ownership(lpMem); 287353944Sdim 288353944Sdim if (!owned_asan) 289353944Sdim owned_rtlalloc = HeapValidate(hHeap, 0, lpMem); 290353944Sdim 291353944Sdim if (owned_asan && !owned_rtlalloc) 292353944Sdim ownershipState = ASAN; 293353944Sdim else if (!owned_asan && owned_rtlalloc) 294353944Sdim ownershipState = RTL; 295353944Sdim else if (!owned_asan && !owned_rtlalloc) 296353944Sdim ownershipState = NEITHER; 297353944Sdim 298353944Sdim // If this heap block which was allocated before the ASAN 299353944Sdim // runtime came up, use the real HeapFree function. 300353944Sdim if (UNLIKELY(!asan_inited)) { 301353944Sdim return reallocFunc(hHeap, dwFlags, lpMem, dwBytes); 302353944Sdim } 303353944Sdim bool only_asan_supported_flags = 304353944Sdim (HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) == 0; 305353944Sdim 306353944Sdim if (ownershipState == RTL || 307353944Sdim (ownershipState == NEITHER && !only_asan_supported_flags)) { 308353944Sdim if (only_asan_supported_flags) { 309353944Sdim // if this is a conversion to ASAN upported flags, transfer this 310353944Sdim // allocation to the ASAN allocator 311353944Sdim void *replacement_alloc; 312353944Sdim if (dwFlags & HEAP_ZERO_MEMORY) 313353944Sdim replacement_alloc = asan_calloc(1, dwBytes, &stack); 314353944Sdim else 315353944Sdim replacement_alloc = asan_malloc(dwBytes, &stack); 316353944Sdim if (replacement_alloc) { 317353944Sdim size_t old_size = heapSizeFunc(hHeap, dwFlags, lpMem); 318353944Sdim if (old_size == ((size_t)0) - 1) { 319353944Sdim asan_free(replacement_alloc, &stack, FROM_MALLOC); 320353944Sdim return nullptr; 321353944Sdim } 322353944Sdim REAL(memcpy)(replacement_alloc, lpMem, old_size); 323353944Sdim freeFunc(hHeap, dwFlags, lpMem); 324353944Sdim } 325353944Sdim return replacement_alloc; 326353944Sdim } else { 327353944Sdim // owned by rtl or neither with unsupported ASAN flags, 328353944Sdim // just pass back to original allocator 329353944Sdim CHECK(ownershipState == RTL || ownershipState == NEITHER); 330353944Sdim CHECK(!only_asan_supported_flags); 331353944Sdim return reallocFunc(hHeap, dwFlags, lpMem, dwBytes); 332353944Sdim } 333353944Sdim } 334353944Sdim 335353944Sdim if (ownershipState == ASAN && !only_asan_supported_flags) { 336353944Sdim // Conversion to unsupported flags allocation, 337353944Sdim // transfer this allocation back to the original allocator. 338353944Sdim void *replacement_alloc = allocFunc(hHeap, dwFlags, dwBytes); 339353944Sdim size_t old_usable_size = 0; 340353944Sdim if (replacement_alloc) { 341353944Sdim old_usable_size = asan_malloc_usable_size(lpMem, pc, bp); 342353944Sdim REAL(memcpy)(replacement_alloc, lpMem, 343353944Sdim Min<size_t>(dwBytes, old_usable_size)); 344353944Sdim asan_free(lpMem, &stack, FROM_MALLOC); 345353944Sdim } 346353944Sdim return replacement_alloc; 347353944Sdim } 348353944Sdim 349353944Sdim CHECK((ownershipState == ASAN || ownershipState == NEITHER) && 350353944Sdim only_asan_supported_flags); 351353944Sdim // At this point we should either be ASAN owned with ASAN supported flags 352353944Sdim // or we owned by neither and have supported flags. 353353944Sdim // Pass through even when it's neither since this could be a null realloc or 354353944Sdim // UAF that ASAN needs to catch. 355353944Sdim } else { 356353944Sdim CHECK((HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) != 0 && 357353944Sdim "unsupported flags"); 358353944Sdim } 359353944Sdim // asan_realloc will never reallocate in place, so for now this flag is 360353944Sdim // unsupported until we figure out a way to fake this. 361353944Sdim if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) 362353944Sdim return nullptr; 363353944Sdim 364353944Sdim // HeapReAlloc and HeapAlloc both happily accept 0 sized allocations. 365353944Sdim // passing a 0 size into asan_realloc will free the allocation. 366353944Sdim // To avoid this and keep behavior consistent, fudge the size if 0. 367353944Sdim // (asan_malloc already does this) 368353944Sdim if (dwBytes == 0) 369353944Sdim dwBytes = 1; 370353944Sdim 371353944Sdim size_t old_size; 372353944Sdim if (dwFlags & HEAP_ZERO_MEMORY) 373353944Sdim old_size = asan_malloc_usable_size(lpMem, pc, bp); 374353944Sdim 375353944Sdim void *ptr = asan_realloc(lpMem, dwBytes, &stack); 376353944Sdim if (ptr == nullptr) 377353944Sdim return nullptr; 378353944Sdim 379353944Sdim if (dwFlags & HEAP_ZERO_MEMORY) { 380353944Sdim size_t new_size = asan_malloc_usable_size(ptr, pc, bp); 381353944Sdim if (old_size < new_size) 382353944Sdim REAL(memset)(((u8 *)ptr) + old_size, 0, new_size - old_size); 383353944Sdim } 384353944Sdim 385353944Sdim return ptr; 386353944Sdim} 387353944Sdim} // namespace __asan 388353944Sdim 389353944SdimINTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags, 390353944Sdim LPVOID lpMem, size_t dwBytes) { 391353944Sdim return SharedReAlloc(REAL(HeapReAlloc), (SizeFunction)REAL(HeapSize), 392353944Sdim REAL(HeapFree), REAL(HeapAlloc), hHeap, dwFlags, lpMem, 393353944Sdim dwBytes); 394353944Sdim} 395353944Sdim 396353944Sdim// The following functions are undocumented and subject to change. 397353944Sdim// However, hooking them is necessary to hook Windows heap 398353944Sdim// allocations with detours and their definitions are unlikely to change. 399353944Sdim// Comments in /minkernel/ntos/rtl/heappublic.c indicate that these functions 400353944Sdim// are part of the heap's public interface. 401353944Sdimtypedef unsigned long LOGICAL; 402353944Sdim 403353944Sdim// This function is documented as part of the Driver Development Kit but *not* 404353944Sdim// the Windows Development Kit. 405353944SdimLOGICAL RtlFreeHeap(void* HeapHandle, DWORD Flags, 406353944Sdim void* BaseAddress); 407353944Sdim 408353944Sdim// This function is documented as part of the Driver Development Kit but *not* 409353944Sdim// the Windows Development Kit. 410353944Sdimvoid* RtlAllocateHeap(void* HeapHandle, DWORD Flags, size_t Size); 411353944Sdim 412353944Sdim// This function is completely undocumented. 413353944Sdimvoid* 414353944SdimRtlReAllocateHeap(void* HeapHandle, DWORD Flags, void* BaseAddress, 415353944Sdim size_t Size); 416353944Sdim 417353944Sdim// This function is completely undocumented. 418353944Sdimsize_t RtlSizeHeap(void* HeapHandle, DWORD Flags, void* BaseAddress); 419353944Sdim 420353944SdimINTERCEPTOR_WINAPI(size_t, RtlSizeHeap, HANDLE HeapHandle, DWORD Flags, 421353944Sdim void* BaseAddress) { 422353944Sdim if (!flags()->windows_hook_rtl_allocators || 423353944Sdim UNLIKELY(!asan_inited || OWNED_BY_RTL(HeapHandle, BaseAddress))) { 424353944Sdim return REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress); 425353944Sdim } 426353944Sdim GET_CURRENT_PC_BP_SP; 427353944Sdim (void)sp; 428353944Sdim return asan_malloc_usable_size(BaseAddress, pc, bp); 429353944Sdim} 430353944Sdim 431353944SdimINTERCEPTOR_WINAPI(BOOL, RtlFreeHeap, HANDLE HeapHandle, DWORD Flags, 432353944Sdim void* BaseAddress) { 433353944Sdim // Heap allocations happen before this function is hooked, so we must fall 434353944Sdim // back to the original function if the pointer is not from the ASAN heap, or 435353944Sdim // unsupported flags are provided. 436353944Sdim if (!flags()->windows_hook_rtl_allocators || 437353944Sdim UNLIKELY((HEAP_FREE_UNSUPPORTED_FLAGS & Flags) != 0 || 438353944Sdim OWNED_BY_RTL(HeapHandle, BaseAddress))) { 439353944Sdim return REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress); 440353944Sdim } 441353944Sdim GET_STACK_TRACE_FREE; 442353944Sdim asan_free(BaseAddress, &stack, FROM_MALLOC); 443353944Sdim return true; 444353944Sdim} 445353944Sdim 446353944SdimINTERCEPTOR_WINAPI(void*, RtlAllocateHeap, HANDLE HeapHandle, DWORD Flags, 447353944Sdim size_t Size) { 448353944Sdim // If the ASAN runtime is not initialized, or we encounter an unsupported 449353944Sdim // flag, fall back to the original allocator. 450353944Sdim if (!flags()->windows_hook_rtl_allocators || 451353944Sdim UNLIKELY(!asan_inited || 452353944Sdim (Flags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) { 453353944Sdim return REAL(RtlAllocateHeap)(HeapHandle, Flags, Size); 454353944Sdim } 455353944Sdim GET_STACK_TRACE_MALLOC; 456353944Sdim void *p; 457353944Sdim // Reading MSDN suggests that the *entire* usable allocation is zeroed out. 458353944Sdim // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. 459353944Sdim // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 460353944Sdim if (Flags & HEAP_ZERO_MEMORY) { 461353944Sdim p = asan_calloc(Size, 1, &stack); 462353944Sdim } else { 463353944Sdim p = asan_malloc(Size, &stack); 464353944Sdim } 465353944Sdim return p; 466353944Sdim} 467353944Sdim 468353944SdimINTERCEPTOR_WINAPI(void*, RtlReAllocateHeap, HANDLE HeapHandle, DWORD Flags, 469353944Sdim void* BaseAddress, size_t Size) { 470353944Sdim // If it's actually a heap block which was allocated before the ASAN runtime 471353944Sdim // came up, use the real RtlFreeHeap function. 472353944Sdim if (!flags()->windows_hook_rtl_allocators) 473353944Sdim return REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size); 474353944Sdim 475353944Sdim return SharedReAlloc(REAL(RtlReAllocateHeap), REAL(RtlSizeHeap), 476353944Sdim REAL(RtlFreeHeap), REAL(RtlAllocateHeap), HeapHandle, 477353944Sdim Flags, BaseAddress, Size); 478353944Sdim} 479353944Sdim 480353944Sdimnamespace __asan { 481353944Sdim 482353944Sdimstatic void TryToOverrideFunction(const char *fname, uptr new_func) { 483353944Sdim // Failure here is not fatal. The CRT may not be present, and different CRT 484353944Sdim // versions use different symbols. 485353944Sdim if (!__interception::OverrideFunction(fname, new_func)) 486353944Sdim VPrintf(2, "Failed to override function %s\n", fname); 487353944Sdim} 488353944Sdim 489353944Sdimvoid ReplaceSystemMalloc() { 490353944Sdim#if defined(ASAN_DYNAMIC) 491353944Sdim TryToOverrideFunction("free", (uptr)free); 492353944Sdim TryToOverrideFunction("_free_base", (uptr)free); 493353944Sdim TryToOverrideFunction("malloc", (uptr)malloc); 494353944Sdim TryToOverrideFunction("_malloc_base", (uptr)malloc); 495353944Sdim TryToOverrideFunction("_malloc_crt", (uptr)malloc); 496353944Sdim TryToOverrideFunction("calloc", (uptr)calloc); 497353944Sdim TryToOverrideFunction("_calloc_base", (uptr)calloc); 498353944Sdim TryToOverrideFunction("_calloc_crt", (uptr)calloc); 499353944Sdim TryToOverrideFunction("realloc", (uptr)realloc); 500353944Sdim TryToOverrideFunction("_realloc_base", (uptr)realloc); 501353944Sdim TryToOverrideFunction("_realloc_crt", (uptr)realloc); 502353944Sdim TryToOverrideFunction("_recalloc", (uptr)_recalloc); 503353944Sdim TryToOverrideFunction("_recalloc_base", (uptr)_recalloc); 504353944Sdim TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc); 505353944Sdim TryToOverrideFunction("_msize", (uptr)_msize); 506353944Sdim TryToOverrideFunction("_msize_base", (uptr)_msize); 507353944Sdim TryToOverrideFunction("_expand", (uptr)_expand); 508353944Sdim TryToOverrideFunction("_expand_base", (uptr)_expand); 509353944Sdim 510353944Sdim if (flags()->windows_hook_rtl_allocators) { 511353944Sdim INTERCEPT_FUNCTION(HeapSize); 512353944Sdim INTERCEPT_FUNCTION(HeapFree); 513353944Sdim INTERCEPT_FUNCTION(HeapReAlloc); 514353944Sdim INTERCEPT_FUNCTION(HeapAlloc); 515353944Sdim 516353944Sdim // Undocumented functions must be intercepted by name, not by symbol. 517353944Sdim __interception::OverrideFunction("RtlSizeHeap", (uptr)WRAP(RtlSizeHeap), 518353944Sdim (uptr *)&REAL(RtlSizeHeap)); 519353944Sdim __interception::OverrideFunction("RtlFreeHeap", (uptr)WRAP(RtlFreeHeap), 520353944Sdim (uptr *)&REAL(RtlFreeHeap)); 521353944Sdim __interception::OverrideFunction("RtlReAllocateHeap", 522353944Sdim (uptr)WRAP(RtlReAllocateHeap), 523353944Sdim (uptr *)&REAL(RtlReAllocateHeap)); 524353944Sdim __interception::OverrideFunction("RtlAllocateHeap", 525353944Sdim (uptr)WRAP(RtlAllocateHeap), 526353944Sdim (uptr *)&REAL(RtlAllocateHeap)); 527353944Sdim } else { 528353944Sdim#define INTERCEPT_UCRT_FUNCTION(func) \ 529353944Sdim if (!INTERCEPT_FUNCTION_DLLIMPORT( \ 530353944Sdim "ucrtbase.dll", "api-ms-win-core-heap-l1-1-0.dll", func)) { \ 531353944Sdim VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func); \ 532353944Sdim } 533353944Sdim INTERCEPT_UCRT_FUNCTION(HeapAlloc); 534353944Sdim INTERCEPT_UCRT_FUNCTION(HeapFree); 535353944Sdim INTERCEPT_UCRT_FUNCTION(HeapReAlloc); 536353944Sdim INTERCEPT_UCRT_FUNCTION(HeapSize); 537353944Sdim#undef INTERCEPT_UCRT_FUNCTION 538353944Sdim } 539353944Sdim // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which 540353944Sdim // enable cross-module inlining. This means our _malloc_base hook won't catch 541353944Sdim // all CRT allocations. This code here patches the import table of 542353944Sdim // ucrtbase.dll so that all attempts to use the lower-level win32 heap 543353944Sdim // allocation API will be directed to ASan's heap. We don't currently 544353944Sdim // intercept all calls to HeapAlloc. If we did, we would have to check on 545353944Sdim // HeapFree whether the pointer came from ASan of from the system. 546353944Sdim 547353944Sdim#endif // defined(ASAN_DYNAMIC) 548353944Sdim} 549353944Sdim} // namespace __asan 550353944Sdim 551353944Sdim#endif // _WIN32 552