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