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