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