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