168d75effSDimitry Andric //===-- asan_malloc_win.cpp -----------------------------------------------===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // This file is a part of AddressSanitizer, an address sanity checker. 1068d75effSDimitry Andric // 1168d75effSDimitry Andric // Windows-specific malloc interception. 1268d75effSDimitry Andric //===----------------------------------------------------------------------===// 1368d75effSDimitry Andric 1468d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_interface.h" 1568d75effSDimitry Andric #include "sanitizer_common/sanitizer_platform.h" 1668d75effSDimitry Andric #if SANITIZER_WINDOWS 1768d75effSDimitry Andric #include "asan_allocator.h" 1868d75effSDimitry Andric #include "asan_interceptors.h" 1968d75effSDimitry Andric #include "asan_internal.h" 2068d75effSDimitry Andric #include "asan_stack.h" 2168d75effSDimitry Andric #include "interception/interception.h" 2268d75effSDimitry Andric #include <stddef.h> 2368d75effSDimitry Andric 2468d75effSDimitry Andric // Intentionally not including windows.h here, to avoid the risk of 2568d75effSDimitry Andric // pulling in conflicting declarations of these functions. (With mingw-w64, 2668d75effSDimitry Andric // there's a risk of windows.h pulling in stdint.h.) 2768d75effSDimitry Andric typedef int BOOL; 2868d75effSDimitry Andric typedef void *HANDLE; 2968d75effSDimitry Andric typedef const void *LPCVOID; 3068d75effSDimitry Andric typedef void *LPVOID; 3168d75effSDimitry Andric 3268d75effSDimitry Andric typedef unsigned long DWORD; 3368d75effSDimitry Andric constexpr unsigned long HEAP_ZERO_MEMORY = 0x00000008; 3468d75effSDimitry Andric constexpr unsigned long HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010; 3568d75effSDimitry Andric constexpr unsigned long HEAP_ALLOCATE_SUPPORTED_FLAGS = (HEAP_ZERO_MEMORY); 3668d75effSDimitry Andric constexpr unsigned long HEAP_ALLOCATE_UNSUPPORTED_FLAGS = 3768d75effSDimitry Andric (~HEAP_ALLOCATE_SUPPORTED_FLAGS); 3868d75effSDimitry Andric constexpr unsigned long HEAP_FREE_UNSUPPORTED_FLAGS = 3968d75effSDimitry Andric (~HEAP_ALLOCATE_SUPPORTED_FLAGS); 4068d75effSDimitry Andric constexpr unsigned long HEAP_REALLOC_UNSUPPORTED_FLAGS = 4168d75effSDimitry Andric (~HEAP_ALLOCATE_SUPPORTED_FLAGS); 4268d75effSDimitry Andric 4368d75effSDimitry Andric 4468d75effSDimitry Andric extern "C" { 4568d75effSDimitry Andric LPVOID WINAPI HeapAlloc(HANDLE hHeap, DWORD dwFlags, size_t dwBytes); 4668d75effSDimitry Andric LPVOID WINAPI HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, 4768d75effSDimitry Andric size_t dwBytes); 4868d75effSDimitry Andric BOOL WINAPI HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); 4968d75effSDimitry Andric size_t WINAPI HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); 5068d75effSDimitry Andric 5168d75effSDimitry Andric BOOL WINAPI HeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); 5268d75effSDimitry Andric } 5368d75effSDimitry Andric 5468d75effSDimitry Andric using namespace __asan; 5568d75effSDimitry Andric 5668d75effSDimitry Andric // MT: Simply defining functions with the same signature in *.obj 5768d75effSDimitry Andric // files overrides the standard functions in the CRT. 5868d75effSDimitry Andric // MD: Memory allocation functions are defined in the CRT .dll, 5968d75effSDimitry Andric // so we have to intercept them before they are called for the first time. 6068d75effSDimitry Andric 6168d75effSDimitry Andric #if ASAN_DYNAMIC 6268d75effSDimitry Andric # define ALLOCATION_FUNCTION_ATTRIBUTE 6368d75effSDimitry Andric #else 6468d75effSDimitry Andric # define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE 6568d75effSDimitry Andric #endif 6668d75effSDimitry Andric 6768d75effSDimitry Andric extern "C" { 6868d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 6968d75effSDimitry Andric size_t _msize(void *ptr) { 7068d75effSDimitry Andric GET_CURRENT_PC_BP_SP; 7168d75effSDimitry Andric (void)sp; 7268d75effSDimitry Andric return asan_malloc_usable_size(ptr, pc, bp); 7368d75effSDimitry Andric } 7468d75effSDimitry Andric 7568d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 7668d75effSDimitry Andric size_t _msize_base(void *ptr) { 7768d75effSDimitry Andric return _msize(ptr); 7868d75effSDimitry Andric } 7968d75effSDimitry Andric 8068d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 8168d75effSDimitry Andric void free(void *ptr) { 8268d75effSDimitry Andric GET_STACK_TRACE_FREE; 8368d75effSDimitry Andric return asan_free(ptr, &stack, FROM_MALLOC); 8468d75effSDimitry Andric } 8568d75effSDimitry Andric 8668d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 8768d75effSDimitry Andric void _free_dbg(void *ptr, int) { 8868d75effSDimitry Andric free(ptr); 8968d75effSDimitry Andric } 9068d75effSDimitry Andric 9168d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 9268d75effSDimitry Andric void _free_base(void *ptr) { 9368d75effSDimitry Andric free(ptr); 9468d75effSDimitry Andric } 9568d75effSDimitry Andric 9668d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 9768d75effSDimitry Andric void *malloc(size_t size) { 9868d75effSDimitry Andric GET_STACK_TRACE_MALLOC; 9968d75effSDimitry Andric return asan_malloc(size, &stack); 10068d75effSDimitry Andric } 10168d75effSDimitry Andric 10268d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 10368d75effSDimitry Andric void *_malloc_base(size_t size) { 10468d75effSDimitry Andric return malloc(size); 10568d75effSDimitry Andric } 10668d75effSDimitry Andric 10768d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 10868d75effSDimitry Andric void *_malloc_dbg(size_t size, int, const char *, int) { 10968d75effSDimitry Andric return malloc(size); 11068d75effSDimitry Andric } 11168d75effSDimitry Andric 11268d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 11368d75effSDimitry Andric void *calloc(size_t nmemb, size_t size) { 11468d75effSDimitry Andric GET_STACK_TRACE_MALLOC; 11568d75effSDimitry Andric return asan_calloc(nmemb, size, &stack); 11668d75effSDimitry Andric } 11768d75effSDimitry Andric 11868d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 11968d75effSDimitry Andric void *_calloc_base(size_t nmemb, size_t size) { 12068d75effSDimitry Andric return calloc(nmemb, size); 12168d75effSDimitry Andric } 12268d75effSDimitry Andric 12368d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 12468d75effSDimitry Andric void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) { 12568d75effSDimitry Andric return calloc(nmemb, size); 12668d75effSDimitry Andric } 12768d75effSDimitry Andric 12868d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 12968d75effSDimitry Andric void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { 13068d75effSDimitry Andric return calloc(nmemb, size); 13168d75effSDimitry Andric } 13268d75effSDimitry Andric 13368d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 13468d75effSDimitry Andric void *realloc(void *ptr, size_t size) { 13568d75effSDimitry Andric GET_STACK_TRACE_MALLOC; 13668d75effSDimitry Andric return asan_realloc(ptr, size, &stack); 13768d75effSDimitry Andric } 13868d75effSDimitry Andric 13968d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 14068d75effSDimitry Andric void *_realloc_dbg(void *ptr, size_t size, int) { 14168d75effSDimitry Andric UNREACHABLE("_realloc_dbg should not exist!"); 14268d75effSDimitry Andric return 0; 14368d75effSDimitry Andric } 14468d75effSDimitry Andric 14568d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 14668d75effSDimitry Andric void *_realloc_base(void *ptr, size_t size) { 14768d75effSDimitry Andric return realloc(ptr, size); 14868d75effSDimitry Andric } 14968d75effSDimitry Andric 15068d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 15168d75effSDimitry Andric void *_recalloc(void *p, size_t n, size_t elem_size) { 15268d75effSDimitry Andric if (!p) 15368d75effSDimitry Andric return calloc(n, elem_size); 15468d75effSDimitry Andric const size_t size = n * elem_size; 15568d75effSDimitry Andric if (elem_size != 0 && size / elem_size != n) 15668d75effSDimitry Andric return 0; 15768d75effSDimitry Andric 15868d75effSDimitry Andric size_t old_size = _msize(p); 15968d75effSDimitry Andric void *new_alloc = malloc(size); 16068d75effSDimitry Andric if (new_alloc) { 16168d75effSDimitry Andric REAL(memcpy)(new_alloc, p, Min<size_t>(size, old_size)); 16268d75effSDimitry Andric if (old_size < size) 16368d75effSDimitry Andric REAL(memset)(((u8 *)new_alloc) + old_size, 0, size - old_size); 16468d75effSDimitry Andric free(p); 16568d75effSDimitry Andric } 16668d75effSDimitry Andric return new_alloc; 16768d75effSDimitry Andric } 16868d75effSDimitry Andric 16968d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 17068d75effSDimitry Andric void *_recalloc_base(void *p, size_t n, size_t elem_size) { 17168d75effSDimitry Andric return _recalloc(p, n, elem_size); 17268d75effSDimitry Andric } 17368d75effSDimitry Andric 17468d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 17568d75effSDimitry Andric void *_expand(void *memblock, size_t size) { 17668d75effSDimitry Andric // _expand is used in realloc-like functions to resize the buffer if possible. 17768d75effSDimitry Andric // We don't want memory to stand still while resizing buffers, so return 0. 17868d75effSDimitry Andric return 0; 17968d75effSDimitry Andric } 18068d75effSDimitry Andric 18168d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE 18268d75effSDimitry Andric void *_expand_dbg(void *memblock, size_t size) { 18368d75effSDimitry Andric return _expand(memblock, size); 18468d75effSDimitry Andric } 18568d75effSDimitry Andric 18668d75effSDimitry Andric // TODO(timurrrr): Might want to add support for _aligned_* allocation 18768d75effSDimitry Andric // functions to detect a bit more bugs. Those functions seem to wrap malloc(). 18868d75effSDimitry Andric 18968d75effSDimitry Andric int _CrtDbgReport(int, const char*, int, 19068d75effSDimitry Andric const char*, const char*, ...) { 19168d75effSDimitry Andric ShowStatsAndAbort(); 19268d75effSDimitry Andric } 19368d75effSDimitry Andric 19468d75effSDimitry Andric int _CrtDbgReportW(int reportType, const wchar_t*, int, 19568d75effSDimitry Andric const wchar_t*, const wchar_t*, ...) { 19668d75effSDimitry Andric ShowStatsAndAbort(); 19768d75effSDimitry Andric } 19868d75effSDimitry Andric 19968d75effSDimitry Andric int _CrtSetReportMode(int, int) { 20068d75effSDimitry Andric return 0; 20168d75effSDimitry Andric } 20268d75effSDimitry Andric } // extern "C" 20368d75effSDimitry Andric 20468d75effSDimitry Andric #define OWNED_BY_RTL(heap, memory) \ 20568d75effSDimitry Andric (!__sanitizer_get_ownership(memory) && HeapValidate(heap, 0, memory)) 20668d75effSDimitry Andric 20768d75effSDimitry Andric INTERCEPTOR_WINAPI(size_t, HeapSize, HANDLE hHeap, DWORD dwFlags, 20868d75effSDimitry Andric LPCVOID lpMem) { 20968d75effSDimitry Andric // If the RTL allocators are hooked we need to check whether the ASAN 21068d75effSDimitry Andric // allocator owns the pointer we're about to use. Allocations occur before 21168d75effSDimitry Andric // interception takes place, so if it is not owned by the RTL heap we can 21268d75effSDimitry Andric // pass it to the ASAN heap for inspection. 21368d75effSDimitry Andric if (flags()->windows_hook_rtl_allocators) { 214*5f757f3fSDimitry Andric if (!AsanInited() || OWNED_BY_RTL(hHeap, lpMem)) 21568d75effSDimitry Andric return REAL(HeapSize)(hHeap, dwFlags, lpMem); 21668d75effSDimitry Andric } else { 21768d75effSDimitry Andric CHECK(dwFlags == 0 && "unsupported heap flags"); 21868d75effSDimitry Andric } 21968d75effSDimitry Andric GET_CURRENT_PC_BP_SP; 22068d75effSDimitry Andric (void)sp; 22168d75effSDimitry Andric return asan_malloc_usable_size(lpMem, pc, bp); 22268d75effSDimitry Andric } 22368d75effSDimitry Andric 22468d75effSDimitry Andric INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags, 22568d75effSDimitry Andric size_t dwBytes) { 22668d75effSDimitry Andric // If the ASAN runtime is not initialized, or we encounter an unsupported 22768d75effSDimitry Andric // flag, fall back to the original allocator. 22868d75effSDimitry Andric if (flags()->windows_hook_rtl_allocators) { 229*5f757f3fSDimitry Andric if (UNLIKELY(!AsanInited() || 23068d75effSDimitry Andric (dwFlags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) { 23168d75effSDimitry Andric return REAL(HeapAlloc)(hHeap, dwFlags, dwBytes); 23268d75effSDimitry Andric } 23368d75effSDimitry Andric } else { 23468d75effSDimitry Andric // In the case that we don't hook the rtl allocators, 23568d75effSDimitry Andric // this becomes an assert since there is no failover to the original 23668d75effSDimitry Andric // allocator. 23768d75effSDimitry Andric CHECK((HEAP_ALLOCATE_UNSUPPORTED_FLAGS & dwFlags) != 0 && 23868d75effSDimitry Andric "unsupported flags"); 23968d75effSDimitry Andric } 24068d75effSDimitry Andric GET_STACK_TRACE_MALLOC; 24168d75effSDimitry Andric void *p = asan_malloc(dwBytes, &stack); 24268d75effSDimitry Andric // Reading MSDN suggests that the *entire* usable allocation is zeroed out. 24368d75effSDimitry Andric // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. 24468d75effSDimitry Andric // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 24568d75effSDimitry Andric if (p && (dwFlags & HEAP_ZERO_MEMORY)) { 24668d75effSDimitry Andric GET_CURRENT_PC_BP_SP; 24768d75effSDimitry Andric (void)sp; 24868d75effSDimitry Andric auto usable_size = asan_malloc_usable_size(p, pc, bp); 24968d75effSDimitry Andric internal_memset(p, 0, usable_size); 25068d75effSDimitry Andric } 25168d75effSDimitry Andric return p; 25268d75effSDimitry Andric } 25368d75effSDimitry Andric 25468d75effSDimitry Andric INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) { 25568d75effSDimitry Andric // Heap allocations happen before this function is hooked, so we must fall 25668d75effSDimitry Andric // back to the original function if the pointer is not from the ASAN heap, 25768d75effSDimitry Andric // or unsupported flags are provided. 25868d75effSDimitry Andric if (flags()->windows_hook_rtl_allocators) { 25968d75effSDimitry Andric if (OWNED_BY_RTL(hHeap, lpMem)) 26068d75effSDimitry Andric return REAL(HeapFree)(hHeap, dwFlags, lpMem); 26168d75effSDimitry Andric } else { 26268d75effSDimitry Andric CHECK((HEAP_FREE_UNSUPPORTED_FLAGS & dwFlags) != 0 && "unsupported flags"); 26368d75effSDimitry Andric } 26468d75effSDimitry Andric GET_STACK_TRACE_FREE; 26568d75effSDimitry Andric asan_free(lpMem, &stack, FROM_MALLOC); 26668d75effSDimitry Andric return true; 26768d75effSDimitry Andric } 26868d75effSDimitry Andric 26968d75effSDimitry Andric namespace __asan { 27068d75effSDimitry Andric using AllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, size_t); 27168d75effSDimitry Andric using ReAllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, LPVOID, size_t); 27268d75effSDimitry Andric using SizeFunction = size_t(WINAPI *)(HANDLE, DWORD, LPVOID); 27368d75effSDimitry Andric using FreeFunction = BOOL(WINAPI *)(HANDLE, DWORD, LPVOID); 27468d75effSDimitry Andric 27568d75effSDimitry Andric void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc, 27668d75effSDimitry Andric FreeFunction freeFunc, AllocFunction allocFunc, 27768d75effSDimitry Andric HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, size_t dwBytes) { 27868d75effSDimitry Andric CHECK(reallocFunc && heapSizeFunc && freeFunc && allocFunc); 27968d75effSDimitry Andric GET_STACK_TRACE_MALLOC; 28068d75effSDimitry Andric GET_CURRENT_PC_BP_SP; 28168d75effSDimitry Andric (void)sp; 28268d75effSDimitry Andric if (flags()->windows_hook_rtl_allocators) { 28368d75effSDimitry Andric enum AllocationOwnership { NEITHER = 0, ASAN = 1, RTL = 2 }; 28468d75effSDimitry Andric AllocationOwnership ownershipState; 28568d75effSDimitry Andric bool owned_rtlalloc = false; 28668d75effSDimitry Andric bool owned_asan = __sanitizer_get_ownership(lpMem); 28768d75effSDimitry Andric 28868d75effSDimitry Andric if (!owned_asan) 28968d75effSDimitry Andric owned_rtlalloc = HeapValidate(hHeap, 0, lpMem); 29068d75effSDimitry Andric 29168d75effSDimitry Andric if (owned_asan && !owned_rtlalloc) 29268d75effSDimitry Andric ownershipState = ASAN; 29368d75effSDimitry Andric else if (!owned_asan && owned_rtlalloc) 29468d75effSDimitry Andric ownershipState = RTL; 29568d75effSDimitry Andric else if (!owned_asan && !owned_rtlalloc) 29668d75effSDimitry Andric ownershipState = NEITHER; 29768d75effSDimitry Andric 29868d75effSDimitry Andric // If this heap block which was allocated before the ASAN 29968d75effSDimitry Andric // runtime came up, use the real HeapFree function. 300*5f757f3fSDimitry Andric if (UNLIKELY(!AsanInited())) { 30168d75effSDimitry Andric return reallocFunc(hHeap, dwFlags, lpMem, dwBytes); 30268d75effSDimitry Andric } 30368d75effSDimitry Andric bool only_asan_supported_flags = 30468d75effSDimitry Andric (HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) == 0; 30568d75effSDimitry Andric 30668d75effSDimitry Andric if (ownershipState == RTL || 30768d75effSDimitry Andric (ownershipState == NEITHER && !only_asan_supported_flags)) { 30868d75effSDimitry Andric if (only_asan_supported_flags) { 30968d75effSDimitry Andric // if this is a conversion to ASAN upported flags, transfer this 31068d75effSDimitry Andric // allocation to the ASAN allocator 31168d75effSDimitry Andric void *replacement_alloc; 31268d75effSDimitry Andric if (dwFlags & HEAP_ZERO_MEMORY) 31368d75effSDimitry Andric replacement_alloc = asan_calloc(1, dwBytes, &stack); 31468d75effSDimitry Andric else 31568d75effSDimitry Andric replacement_alloc = asan_malloc(dwBytes, &stack); 31668d75effSDimitry Andric if (replacement_alloc) { 31768d75effSDimitry Andric size_t old_size = heapSizeFunc(hHeap, dwFlags, lpMem); 31868d75effSDimitry Andric if (old_size == ((size_t)0) - 1) { 31968d75effSDimitry Andric asan_free(replacement_alloc, &stack, FROM_MALLOC); 32068d75effSDimitry Andric return nullptr; 32168d75effSDimitry Andric } 32268d75effSDimitry Andric REAL(memcpy)(replacement_alloc, lpMem, old_size); 32368d75effSDimitry Andric freeFunc(hHeap, dwFlags, lpMem); 32468d75effSDimitry Andric } 32568d75effSDimitry Andric return replacement_alloc; 32668d75effSDimitry Andric } else { 32768d75effSDimitry Andric // owned by rtl or neither with unsupported ASAN flags, 32868d75effSDimitry Andric // just pass back to original allocator 32968d75effSDimitry Andric CHECK(ownershipState == RTL || ownershipState == NEITHER); 33068d75effSDimitry Andric CHECK(!only_asan_supported_flags); 33168d75effSDimitry Andric return reallocFunc(hHeap, dwFlags, lpMem, dwBytes); 33268d75effSDimitry Andric } 33368d75effSDimitry Andric } 33468d75effSDimitry Andric 33568d75effSDimitry Andric if (ownershipState == ASAN && !only_asan_supported_flags) { 33668d75effSDimitry Andric // Conversion to unsupported flags allocation, 33768d75effSDimitry Andric // transfer this allocation back to the original allocator. 33868d75effSDimitry Andric void *replacement_alloc = allocFunc(hHeap, dwFlags, dwBytes); 33968d75effSDimitry Andric size_t old_usable_size = 0; 34068d75effSDimitry Andric if (replacement_alloc) { 34168d75effSDimitry Andric old_usable_size = asan_malloc_usable_size(lpMem, pc, bp); 34268d75effSDimitry Andric REAL(memcpy)(replacement_alloc, lpMem, 34368d75effSDimitry Andric Min<size_t>(dwBytes, old_usable_size)); 34468d75effSDimitry Andric asan_free(lpMem, &stack, FROM_MALLOC); 34568d75effSDimitry Andric } 34668d75effSDimitry Andric return replacement_alloc; 34768d75effSDimitry Andric } 34868d75effSDimitry Andric 34968d75effSDimitry Andric CHECK((ownershipState == ASAN || ownershipState == NEITHER) && 35068d75effSDimitry Andric only_asan_supported_flags); 35168d75effSDimitry Andric // At this point we should either be ASAN owned with ASAN supported flags 35268d75effSDimitry Andric // or we owned by neither and have supported flags. 35368d75effSDimitry Andric // Pass through even when it's neither since this could be a null realloc or 35468d75effSDimitry Andric // UAF that ASAN needs to catch. 35568d75effSDimitry Andric } else { 35668d75effSDimitry Andric CHECK((HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) != 0 && 35768d75effSDimitry Andric "unsupported flags"); 35868d75effSDimitry Andric } 35968d75effSDimitry Andric // asan_realloc will never reallocate in place, so for now this flag is 36068d75effSDimitry Andric // unsupported until we figure out a way to fake this. 36168d75effSDimitry Andric if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) 36268d75effSDimitry Andric return nullptr; 36368d75effSDimitry Andric 36468d75effSDimitry Andric // HeapReAlloc and HeapAlloc both happily accept 0 sized allocations. 36568d75effSDimitry Andric // passing a 0 size into asan_realloc will free the allocation. 36668d75effSDimitry Andric // To avoid this and keep behavior consistent, fudge the size if 0. 36768d75effSDimitry Andric // (asan_malloc already does this) 36868d75effSDimitry Andric if (dwBytes == 0) 36968d75effSDimitry Andric dwBytes = 1; 37068d75effSDimitry Andric 37168d75effSDimitry Andric size_t old_size; 37268d75effSDimitry Andric if (dwFlags & HEAP_ZERO_MEMORY) 37368d75effSDimitry Andric old_size = asan_malloc_usable_size(lpMem, pc, bp); 37468d75effSDimitry Andric 37568d75effSDimitry Andric void *ptr = asan_realloc(lpMem, dwBytes, &stack); 37668d75effSDimitry Andric if (ptr == nullptr) 37768d75effSDimitry Andric return nullptr; 37868d75effSDimitry Andric 37968d75effSDimitry Andric if (dwFlags & HEAP_ZERO_MEMORY) { 38068d75effSDimitry Andric size_t new_size = asan_malloc_usable_size(ptr, pc, bp); 38168d75effSDimitry Andric if (old_size < new_size) 38268d75effSDimitry Andric REAL(memset)(((u8 *)ptr) + old_size, 0, new_size - old_size); 38368d75effSDimitry Andric } 38468d75effSDimitry Andric 38568d75effSDimitry Andric return ptr; 38668d75effSDimitry Andric } 38768d75effSDimitry Andric } // namespace __asan 38868d75effSDimitry Andric 38968d75effSDimitry Andric INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags, 39068d75effSDimitry Andric LPVOID lpMem, size_t dwBytes) { 39168d75effSDimitry Andric return SharedReAlloc(REAL(HeapReAlloc), (SizeFunction)REAL(HeapSize), 39268d75effSDimitry Andric REAL(HeapFree), REAL(HeapAlloc), hHeap, dwFlags, lpMem, 39368d75effSDimitry Andric dwBytes); 39468d75effSDimitry Andric } 39568d75effSDimitry Andric 39668d75effSDimitry Andric // The following functions are undocumented and subject to change. 39768d75effSDimitry Andric // However, hooking them is necessary to hook Windows heap 39868d75effSDimitry Andric // allocations with detours and their definitions are unlikely to change. 39968d75effSDimitry Andric // Comments in /minkernel/ntos/rtl/heappublic.c indicate that these functions 40068d75effSDimitry Andric // are part of the heap's public interface. 40168d75effSDimitry Andric typedef unsigned long LOGICAL; 40268d75effSDimitry Andric 40368d75effSDimitry Andric // This function is documented as part of the Driver Development Kit but *not* 40468d75effSDimitry Andric // the Windows Development Kit. 40568d75effSDimitry Andric LOGICAL RtlFreeHeap(void* HeapHandle, DWORD Flags, 40668d75effSDimitry Andric void* BaseAddress); 40768d75effSDimitry Andric 40868d75effSDimitry Andric // This function is documented as part of the Driver Development Kit but *not* 40968d75effSDimitry Andric // the Windows Development Kit. 41068d75effSDimitry Andric void* RtlAllocateHeap(void* HeapHandle, DWORD Flags, size_t Size); 41168d75effSDimitry Andric 41268d75effSDimitry Andric // This function is completely undocumented. 41368d75effSDimitry Andric void* 41468d75effSDimitry Andric RtlReAllocateHeap(void* HeapHandle, DWORD Flags, void* BaseAddress, 41568d75effSDimitry Andric size_t Size); 41668d75effSDimitry Andric 41768d75effSDimitry Andric // This function is completely undocumented. 41868d75effSDimitry Andric size_t RtlSizeHeap(void* HeapHandle, DWORD Flags, void* BaseAddress); 41968d75effSDimitry Andric 42068d75effSDimitry Andric INTERCEPTOR_WINAPI(size_t, RtlSizeHeap, HANDLE HeapHandle, DWORD Flags, 42168d75effSDimitry Andric void* BaseAddress) { 42268d75effSDimitry Andric if (!flags()->windows_hook_rtl_allocators || 423*5f757f3fSDimitry Andric UNLIKELY(!AsanInited() || OWNED_BY_RTL(HeapHandle, BaseAddress))) { 42468d75effSDimitry Andric return REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress); 42568d75effSDimitry Andric } 42668d75effSDimitry Andric GET_CURRENT_PC_BP_SP; 42768d75effSDimitry Andric (void)sp; 42868d75effSDimitry Andric return asan_malloc_usable_size(BaseAddress, pc, bp); 42968d75effSDimitry Andric } 43068d75effSDimitry Andric 43168d75effSDimitry Andric INTERCEPTOR_WINAPI(BOOL, RtlFreeHeap, HANDLE HeapHandle, DWORD Flags, 43268d75effSDimitry Andric void* BaseAddress) { 43368d75effSDimitry Andric // Heap allocations happen before this function is hooked, so we must fall 43468d75effSDimitry Andric // back to the original function if the pointer is not from the ASAN heap, or 43568d75effSDimitry Andric // unsupported flags are provided. 43668d75effSDimitry Andric if (!flags()->windows_hook_rtl_allocators || 43768d75effSDimitry Andric UNLIKELY((HEAP_FREE_UNSUPPORTED_FLAGS & Flags) != 0 || 43868d75effSDimitry Andric OWNED_BY_RTL(HeapHandle, BaseAddress))) { 43968d75effSDimitry Andric return REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress); 44068d75effSDimitry Andric } 44168d75effSDimitry Andric GET_STACK_TRACE_FREE; 44268d75effSDimitry Andric asan_free(BaseAddress, &stack, FROM_MALLOC); 44368d75effSDimitry Andric return true; 44468d75effSDimitry Andric } 44568d75effSDimitry Andric 44668d75effSDimitry Andric INTERCEPTOR_WINAPI(void*, RtlAllocateHeap, HANDLE HeapHandle, DWORD Flags, 44768d75effSDimitry Andric size_t Size) { 44868d75effSDimitry Andric // If the ASAN runtime is not initialized, or we encounter an unsupported 44968d75effSDimitry Andric // flag, fall back to the original allocator. 45068d75effSDimitry Andric if (!flags()->windows_hook_rtl_allocators || 451*5f757f3fSDimitry Andric UNLIKELY(!AsanInited() || 45268d75effSDimitry Andric (Flags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) { 45368d75effSDimitry Andric return REAL(RtlAllocateHeap)(HeapHandle, Flags, Size); 45468d75effSDimitry Andric } 45568d75effSDimitry Andric GET_STACK_TRACE_MALLOC; 45668d75effSDimitry Andric void *p; 45768d75effSDimitry Andric // Reading MSDN suggests that the *entire* usable allocation is zeroed out. 45868d75effSDimitry Andric // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. 45968d75effSDimitry Andric // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 46068d75effSDimitry Andric if (Flags & HEAP_ZERO_MEMORY) { 46168d75effSDimitry Andric p = asan_calloc(Size, 1, &stack); 46268d75effSDimitry Andric } else { 46368d75effSDimitry Andric p = asan_malloc(Size, &stack); 46468d75effSDimitry Andric } 46568d75effSDimitry Andric return p; 46668d75effSDimitry Andric } 46768d75effSDimitry Andric 46868d75effSDimitry Andric INTERCEPTOR_WINAPI(void*, RtlReAllocateHeap, HANDLE HeapHandle, DWORD Flags, 46968d75effSDimitry Andric void* BaseAddress, size_t Size) { 47068d75effSDimitry Andric // If it's actually a heap block which was allocated before the ASAN runtime 47168d75effSDimitry Andric // came up, use the real RtlFreeHeap function. 47268d75effSDimitry Andric if (!flags()->windows_hook_rtl_allocators) 47368d75effSDimitry Andric return REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size); 47468d75effSDimitry Andric 47568d75effSDimitry Andric return SharedReAlloc(REAL(RtlReAllocateHeap), REAL(RtlSizeHeap), 47668d75effSDimitry Andric REAL(RtlFreeHeap), REAL(RtlAllocateHeap), HeapHandle, 47768d75effSDimitry Andric Flags, BaseAddress, Size); 47868d75effSDimitry Andric } 47968d75effSDimitry Andric 48068d75effSDimitry Andric namespace __asan { 48168d75effSDimitry Andric 48268d75effSDimitry Andric static void TryToOverrideFunction(const char *fname, uptr new_func) { 48368d75effSDimitry Andric // Failure here is not fatal. The CRT may not be present, and different CRT 48468d75effSDimitry Andric // versions use different symbols. 48568d75effSDimitry Andric if (!__interception::OverrideFunction(fname, new_func)) 48668d75effSDimitry Andric VPrintf(2, "Failed to override function %s\n", fname); 48768d75effSDimitry Andric } 48868d75effSDimitry Andric 48968d75effSDimitry Andric void ReplaceSystemMalloc() { 49068d75effSDimitry Andric #if defined(ASAN_DYNAMIC) 49168d75effSDimitry Andric TryToOverrideFunction("free", (uptr)free); 49268d75effSDimitry Andric TryToOverrideFunction("_free_base", (uptr)free); 49368d75effSDimitry Andric TryToOverrideFunction("malloc", (uptr)malloc); 49468d75effSDimitry Andric TryToOverrideFunction("_malloc_base", (uptr)malloc); 49568d75effSDimitry Andric TryToOverrideFunction("_malloc_crt", (uptr)malloc); 49668d75effSDimitry Andric TryToOverrideFunction("calloc", (uptr)calloc); 49768d75effSDimitry Andric TryToOverrideFunction("_calloc_base", (uptr)calloc); 49868d75effSDimitry Andric TryToOverrideFunction("_calloc_crt", (uptr)calloc); 49968d75effSDimitry Andric TryToOverrideFunction("realloc", (uptr)realloc); 50068d75effSDimitry Andric TryToOverrideFunction("_realloc_base", (uptr)realloc); 50168d75effSDimitry Andric TryToOverrideFunction("_realloc_crt", (uptr)realloc); 50268d75effSDimitry Andric TryToOverrideFunction("_recalloc", (uptr)_recalloc); 50368d75effSDimitry Andric TryToOverrideFunction("_recalloc_base", (uptr)_recalloc); 50468d75effSDimitry Andric TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc); 50568d75effSDimitry Andric TryToOverrideFunction("_msize", (uptr)_msize); 50668d75effSDimitry Andric TryToOverrideFunction("_msize_base", (uptr)_msize); 50768d75effSDimitry Andric TryToOverrideFunction("_expand", (uptr)_expand); 50868d75effSDimitry Andric TryToOverrideFunction("_expand_base", (uptr)_expand); 50968d75effSDimitry Andric 51068d75effSDimitry Andric if (flags()->windows_hook_rtl_allocators) { 51106c3fb27SDimitry Andric ASAN_INTERCEPT_FUNC(HeapSize); 51206c3fb27SDimitry Andric ASAN_INTERCEPT_FUNC(HeapFree); 51306c3fb27SDimitry Andric ASAN_INTERCEPT_FUNC(HeapReAlloc); 51406c3fb27SDimitry Andric ASAN_INTERCEPT_FUNC(HeapAlloc); 51568d75effSDimitry Andric 51668d75effSDimitry Andric // Undocumented functions must be intercepted by name, not by symbol. 51768d75effSDimitry Andric __interception::OverrideFunction("RtlSizeHeap", (uptr)WRAP(RtlSizeHeap), 51868d75effSDimitry Andric (uptr *)&REAL(RtlSizeHeap)); 51968d75effSDimitry Andric __interception::OverrideFunction("RtlFreeHeap", (uptr)WRAP(RtlFreeHeap), 52068d75effSDimitry Andric (uptr *)&REAL(RtlFreeHeap)); 52168d75effSDimitry Andric __interception::OverrideFunction("RtlReAllocateHeap", 52268d75effSDimitry Andric (uptr)WRAP(RtlReAllocateHeap), 52368d75effSDimitry Andric (uptr *)&REAL(RtlReAllocateHeap)); 52468d75effSDimitry Andric __interception::OverrideFunction("RtlAllocateHeap", 52568d75effSDimitry Andric (uptr)WRAP(RtlAllocateHeap), 52668d75effSDimitry Andric (uptr *)&REAL(RtlAllocateHeap)); 52768d75effSDimitry Andric } else { 52868d75effSDimitry Andric #define INTERCEPT_UCRT_FUNCTION(func) \ 52968d75effSDimitry Andric if (!INTERCEPT_FUNCTION_DLLIMPORT( \ 53068d75effSDimitry Andric "ucrtbase.dll", "api-ms-win-core-heap-l1-1-0.dll", func)) { \ 53168d75effSDimitry Andric VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func); \ 53268d75effSDimitry Andric } 53368d75effSDimitry Andric INTERCEPT_UCRT_FUNCTION(HeapAlloc); 53468d75effSDimitry Andric INTERCEPT_UCRT_FUNCTION(HeapFree); 53568d75effSDimitry Andric INTERCEPT_UCRT_FUNCTION(HeapReAlloc); 53668d75effSDimitry Andric INTERCEPT_UCRT_FUNCTION(HeapSize); 53768d75effSDimitry Andric #undef INTERCEPT_UCRT_FUNCTION 53868d75effSDimitry Andric } 53968d75effSDimitry Andric // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which 54068d75effSDimitry Andric // enable cross-module inlining. This means our _malloc_base hook won't catch 54168d75effSDimitry Andric // all CRT allocations. This code here patches the import table of 54268d75effSDimitry Andric // ucrtbase.dll so that all attempts to use the lower-level win32 heap 54368d75effSDimitry Andric // allocation API will be directed to ASan's heap. We don't currently 54468d75effSDimitry Andric // intercept all calls to HeapAlloc. If we did, we would have to check on 54568d75effSDimitry Andric // HeapFree whether the pointer came from ASan of from the system. 54668d75effSDimitry Andric 54768d75effSDimitry Andric #endif // defined(ASAN_DYNAMIC) 54868d75effSDimitry Andric } 54968d75effSDimitry Andric } // namespace __asan 55068d75effSDimitry Andric 55168d75effSDimitry Andric #endif // _WIN32 552