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