xref: /freebsd/contrib/llvm-project/compiler-rt/lib/asan/asan_malloc_win.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
168d75effSDimitry Andric //===-- asan_malloc_win.cpp -----------------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is a part of AddressSanitizer, an address sanity checker.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric // Windows-specific malloc interception.
1268d75effSDimitry Andric //===----------------------------------------------------------------------===//
1368d75effSDimitry Andric 
1468d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_interface.h"
1568d75effSDimitry Andric #include "sanitizer_common/sanitizer_platform.h"
1668d75effSDimitry Andric #if SANITIZER_WINDOWS
1768d75effSDimitry Andric #include "asan_allocator.h"
1868d75effSDimitry Andric #include "asan_interceptors.h"
1968d75effSDimitry Andric #include "asan_internal.h"
2068d75effSDimitry Andric #include "asan_stack.h"
2168d75effSDimitry Andric #include "interception/interception.h"
2268d75effSDimitry Andric #include <stddef.h>
2368d75effSDimitry Andric 
2468d75effSDimitry Andric // Intentionally not including windows.h here, to avoid the risk of
2568d75effSDimitry Andric // pulling in conflicting declarations of these functions. (With mingw-w64,
2668d75effSDimitry Andric // there's a risk of windows.h pulling in stdint.h.)
2768d75effSDimitry Andric typedef int BOOL;
2868d75effSDimitry Andric typedef void *HANDLE;
2968d75effSDimitry Andric typedef const void *LPCVOID;
3068d75effSDimitry Andric typedef void *LPVOID;
3168d75effSDimitry Andric 
3268d75effSDimitry Andric typedef unsigned long DWORD;
3368d75effSDimitry Andric constexpr unsigned long HEAP_ZERO_MEMORY = 0x00000008;
3468d75effSDimitry Andric constexpr unsigned long HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010;
3568d75effSDimitry Andric constexpr unsigned long HEAP_ALLOCATE_SUPPORTED_FLAGS = (HEAP_ZERO_MEMORY);
3668d75effSDimitry Andric constexpr unsigned long HEAP_ALLOCATE_UNSUPPORTED_FLAGS =
3768d75effSDimitry Andric     (~HEAP_ALLOCATE_SUPPORTED_FLAGS);
3868d75effSDimitry Andric constexpr unsigned long HEAP_FREE_UNSUPPORTED_FLAGS =
3968d75effSDimitry Andric     (~HEAP_ALLOCATE_SUPPORTED_FLAGS);
4068d75effSDimitry Andric constexpr unsigned long HEAP_REALLOC_UNSUPPORTED_FLAGS =
4168d75effSDimitry Andric     (~HEAP_ALLOCATE_SUPPORTED_FLAGS);
4268d75effSDimitry Andric 
4368d75effSDimitry Andric 
4468d75effSDimitry Andric extern "C" {
4568d75effSDimitry Andric LPVOID WINAPI HeapAlloc(HANDLE hHeap, DWORD dwFlags, size_t dwBytes);
4668d75effSDimitry Andric LPVOID WINAPI HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem,
4768d75effSDimitry Andric                          size_t dwBytes);
4868d75effSDimitry Andric BOOL WINAPI HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
4968d75effSDimitry Andric size_t WINAPI HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
5068d75effSDimitry Andric 
5168d75effSDimitry Andric BOOL WINAPI HeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
5268d75effSDimitry Andric }
5368d75effSDimitry Andric 
5468d75effSDimitry Andric using namespace __asan;
5568d75effSDimitry Andric 
5668d75effSDimitry Andric // MT: Simply defining functions with the same signature in *.obj
5768d75effSDimitry Andric // files overrides the standard functions in the CRT.
5868d75effSDimitry Andric // MD: Memory allocation functions are defined in the CRT .dll,
5968d75effSDimitry Andric // so we have to intercept them before they are called for the first time.
6068d75effSDimitry Andric 
6168d75effSDimitry Andric #if ASAN_DYNAMIC
6268d75effSDimitry Andric # define ALLOCATION_FUNCTION_ATTRIBUTE
6368d75effSDimitry Andric #else
6468d75effSDimitry Andric # define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
6568d75effSDimitry Andric #endif
6668d75effSDimitry Andric 
6768d75effSDimitry Andric extern "C" {
6868d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
6968d75effSDimitry Andric size_t _msize(void *ptr) {
7068d75effSDimitry Andric   GET_CURRENT_PC_BP_SP;
7168d75effSDimitry Andric   (void)sp;
7268d75effSDimitry Andric   return asan_malloc_usable_size(ptr, pc, bp);
7368d75effSDimitry Andric }
7468d75effSDimitry Andric 
7568d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
7668d75effSDimitry Andric size_t _msize_base(void *ptr) {
7768d75effSDimitry Andric   return _msize(ptr);
7868d75effSDimitry Andric }
7968d75effSDimitry Andric 
8068d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
8168d75effSDimitry Andric void free(void *ptr) {
8268d75effSDimitry Andric   GET_STACK_TRACE_FREE;
8368d75effSDimitry Andric   return asan_free(ptr, &stack, FROM_MALLOC);
8468d75effSDimitry Andric }
8568d75effSDimitry Andric 
8668d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
8768d75effSDimitry Andric void _free_dbg(void *ptr, int) {
8868d75effSDimitry Andric   free(ptr);
8968d75effSDimitry Andric }
9068d75effSDimitry Andric 
9168d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
9268d75effSDimitry Andric void _free_base(void *ptr) {
9368d75effSDimitry Andric   free(ptr);
9468d75effSDimitry Andric }
9568d75effSDimitry Andric 
9668d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
9768d75effSDimitry Andric void *malloc(size_t size) {
9868d75effSDimitry Andric   GET_STACK_TRACE_MALLOC;
9968d75effSDimitry Andric   return asan_malloc(size, &stack);
10068d75effSDimitry Andric }
10168d75effSDimitry Andric 
10268d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
10368d75effSDimitry Andric void *_malloc_base(size_t size) {
10468d75effSDimitry Andric   return malloc(size);
10568d75effSDimitry Andric }
10668d75effSDimitry Andric 
10768d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
10868d75effSDimitry Andric void *_malloc_dbg(size_t size, int, const char *, int) {
10968d75effSDimitry Andric   return malloc(size);
11068d75effSDimitry Andric }
11168d75effSDimitry Andric 
11268d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
11368d75effSDimitry Andric void *calloc(size_t nmemb, size_t size) {
11468d75effSDimitry Andric   GET_STACK_TRACE_MALLOC;
11568d75effSDimitry Andric   return asan_calloc(nmemb, size, &stack);
11668d75effSDimitry Andric }
11768d75effSDimitry Andric 
11868d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
11968d75effSDimitry Andric void *_calloc_base(size_t nmemb, size_t size) {
12068d75effSDimitry Andric   return calloc(nmemb, size);
12168d75effSDimitry Andric }
12268d75effSDimitry Andric 
12368d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
12468d75effSDimitry Andric void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
12568d75effSDimitry Andric   return calloc(nmemb, size);
12668d75effSDimitry Andric }
12768d75effSDimitry Andric 
12868d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
12968d75effSDimitry Andric void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
13068d75effSDimitry Andric   return calloc(nmemb, size);
13168d75effSDimitry Andric }
13268d75effSDimitry Andric 
13368d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
13468d75effSDimitry Andric void *realloc(void *ptr, size_t size) {
13568d75effSDimitry Andric   GET_STACK_TRACE_MALLOC;
13668d75effSDimitry Andric   return asan_realloc(ptr, size, &stack);
13768d75effSDimitry Andric }
13868d75effSDimitry Andric 
13968d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
14068d75effSDimitry Andric void *_realloc_dbg(void *ptr, size_t size, int) {
14168d75effSDimitry Andric   UNREACHABLE("_realloc_dbg should not exist!");
14268d75effSDimitry Andric   return 0;
14368d75effSDimitry Andric }
14468d75effSDimitry Andric 
14568d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
14668d75effSDimitry Andric void *_realloc_base(void *ptr, size_t size) {
14768d75effSDimitry Andric   return realloc(ptr, size);
14868d75effSDimitry Andric }
14968d75effSDimitry Andric 
15068d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
15168d75effSDimitry Andric void *_recalloc(void *p, size_t n, size_t elem_size) {
15268d75effSDimitry Andric   if (!p)
15368d75effSDimitry Andric     return calloc(n, elem_size);
15468d75effSDimitry Andric   const size_t size = n * elem_size;
15568d75effSDimitry Andric   if (elem_size != 0 && size / elem_size != n)
15668d75effSDimitry Andric     return 0;
15768d75effSDimitry Andric 
15868d75effSDimitry Andric   size_t old_size = _msize(p);
15968d75effSDimitry Andric   void *new_alloc = malloc(size);
16068d75effSDimitry Andric   if (new_alloc) {
16168d75effSDimitry Andric     REAL(memcpy)(new_alloc, p, Min<size_t>(size, old_size));
16268d75effSDimitry Andric     if (old_size < size)
16368d75effSDimitry Andric       REAL(memset)(((u8 *)new_alloc) + old_size, 0, size - old_size);
16468d75effSDimitry Andric     free(p);
16568d75effSDimitry Andric   }
16668d75effSDimitry Andric   return new_alloc;
16768d75effSDimitry Andric }
16868d75effSDimitry Andric 
16968d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
17068d75effSDimitry Andric void *_recalloc_base(void *p, size_t n, size_t elem_size) {
17168d75effSDimitry Andric   return _recalloc(p, n, elem_size);
17268d75effSDimitry Andric }
17368d75effSDimitry Andric 
17468d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
17568d75effSDimitry Andric void *_expand(void *memblock, size_t size) {
17668d75effSDimitry Andric   // _expand is used in realloc-like functions to resize the buffer if possible.
17768d75effSDimitry Andric   // We don't want memory to stand still while resizing buffers, so return 0.
17868d75effSDimitry Andric   return 0;
17968d75effSDimitry Andric }
18068d75effSDimitry Andric 
18168d75effSDimitry Andric ALLOCATION_FUNCTION_ATTRIBUTE
18268d75effSDimitry Andric void *_expand_dbg(void *memblock, size_t size) {
18368d75effSDimitry Andric   return _expand(memblock, size);
18468d75effSDimitry Andric }
18568d75effSDimitry Andric 
18668d75effSDimitry Andric // TODO(timurrrr): Might want to add support for _aligned_* allocation
18768d75effSDimitry Andric // functions to detect a bit more bugs.  Those functions seem to wrap malloc().
18868d75effSDimitry Andric 
18968d75effSDimitry Andric int _CrtDbgReport(int, const char*, int,
19068d75effSDimitry Andric                   const char*, const char*, ...) {
19168d75effSDimitry Andric   ShowStatsAndAbort();
19268d75effSDimitry Andric }
19368d75effSDimitry Andric 
19468d75effSDimitry Andric int _CrtDbgReportW(int reportType, const wchar_t*, int,
19568d75effSDimitry Andric                    const wchar_t*, const wchar_t*, ...) {
19668d75effSDimitry Andric   ShowStatsAndAbort();
19768d75effSDimitry Andric }
19868d75effSDimitry Andric 
19968d75effSDimitry Andric int _CrtSetReportMode(int, int) {
20068d75effSDimitry Andric   return 0;
20168d75effSDimitry Andric }
20268d75effSDimitry Andric }  // extern "C"
20368d75effSDimitry Andric 
20468d75effSDimitry Andric #define OWNED_BY_RTL(heap, memory) \
20568d75effSDimitry Andric   (!__sanitizer_get_ownership(memory) && HeapValidate(heap, 0, memory))
20668d75effSDimitry Andric 
20768d75effSDimitry Andric INTERCEPTOR_WINAPI(size_t, HeapSize, HANDLE hHeap, DWORD dwFlags,
20868d75effSDimitry Andric                    LPCVOID lpMem) {
20968d75effSDimitry Andric   // If the RTL allocators are hooked we need to check whether the ASAN
21068d75effSDimitry Andric   // allocator owns the pointer we're about to use. Allocations occur before
21168d75effSDimitry Andric   // interception takes place, so if it is not owned by the RTL heap we can
21268d75effSDimitry Andric   // pass it to the ASAN heap for inspection.
21368d75effSDimitry Andric   if (flags()->windows_hook_rtl_allocators) {
214*5f757f3fSDimitry Andric     if (!AsanInited() || OWNED_BY_RTL(hHeap, lpMem))
21568d75effSDimitry Andric       return REAL(HeapSize)(hHeap, dwFlags, lpMem);
21668d75effSDimitry Andric   } else {
21768d75effSDimitry Andric     CHECK(dwFlags == 0 && "unsupported heap flags");
21868d75effSDimitry Andric   }
21968d75effSDimitry Andric   GET_CURRENT_PC_BP_SP;
22068d75effSDimitry Andric   (void)sp;
22168d75effSDimitry Andric   return asan_malloc_usable_size(lpMem, pc, bp);
22268d75effSDimitry Andric }
22368d75effSDimitry Andric 
22468d75effSDimitry Andric INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags,
22568d75effSDimitry Andric                    size_t dwBytes) {
22668d75effSDimitry Andric   // If the ASAN runtime is not initialized, or we encounter an unsupported
22768d75effSDimitry Andric   // flag, fall back to the original allocator.
22868d75effSDimitry Andric   if (flags()->windows_hook_rtl_allocators) {
229*5f757f3fSDimitry Andric     if (UNLIKELY(!AsanInited() ||
23068d75effSDimitry Andric                  (dwFlags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) {
23168d75effSDimitry Andric       return REAL(HeapAlloc)(hHeap, dwFlags, dwBytes);
23268d75effSDimitry Andric     }
23368d75effSDimitry Andric   } else {
23468d75effSDimitry Andric     // In the case that we don't hook the rtl allocators,
23568d75effSDimitry Andric     // this becomes an assert since there is no failover to the original
23668d75effSDimitry Andric     // allocator.
23768d75effSDimitry Andric     CHECK((HEAP_ALLOCATE_UNSUPPORTED_FLAGS & dwFlags) != 0 &&
23868d75effSDimitry Andric           "unsupported flags");
23968d75effSDimitry Andric   }
24068d75effSDimitry Andric   GET_STACK_TRACE_MALLOC;
24168d75effSDimitry Andric   void *p = asan_malloc(dwBytes, &stack);
24268d75effSDimitry Andric   // Reading MSDN suggests that the *entire* usable allocation is zeroed out.
24368d75effSDimitry Andric   // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY.
24468d75effSDimitry Andric   // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083
24568d75effSDimitry Andric   if (p && (dwFlags & HEAP_ZERO_MEMORY)) {
24668d75effSDimitry Andric     GET_CURRENT_PC_BP_SP;
24768d75effSDimitry Andric     (void)sp;
24868d75effSDimitry Andric     auto usable_size = asan_malloc_usable_size(p, pc, bp);
24968d75effSDimitry Andric     internal_memset(p, 0, usable_size);
25068d75effSDimitry Andric   }
25168d75effSDimitry Andric   return p;
25268d75effSDimitry Andric }
25368d75effSDimitry Andric 
25468d75effSDimitry Andric INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) {
25568d75effSDimitry Andric   // Heap allocations happen before this function is hooked, so we must fall
25668d75effSDimitry Andric   // back to the original function if the pointer is not from the ASAN heap,
25768d75effSDimitry Andric   // or unsupported flags are provided.
25868d75effSDimitry Andric   if (flags()->windows_hook_rtl_allocators) {
25968d75effSDimitry Andric     if (OWNED_BY_RTL(hHeap, lpMem))
26068d75effSDimitry Andric       return REAL(HeapFree)(hHeap, dwFlags, lpMem);
26168d75effSDimitry Andric   } else {
26268d75effSDimitry Andric     CHECK((HEAP_FREE_UNSUPPORTED_FLAGS & dwFlags) != 0 && "unsupported flags");
26368d75effSDimitry Andric   }
26468d75effSDimitry Andric   GET_STACK_TRACE_FREE;
26568d75effSDimitry Andric   asan_free(lpMem, &stack, FROM_MALLOC);
26668d75effSDimitry Andric   return true;
26768d75effSDimitry Andric }
26868d75effSDimitry Andric 
26968d75effSDimitry Andric namespace __asan {
27068d75effSDimitry Andric using AllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, size_t);
27168d75effSDimitry Andric using ReAllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, LPVOID, size_t);
27268d75effSDimitry Andric using SizeFunction = size_t(WINAPI *)(HANDLE, DWORD, LPVOID);
27368d75effSDimitry Andric using FreeFunction = BOOL(WINAPI *)(HANDLE, DWORD, LPVOID);
27468d75effSDimitry Andric 
27568d75effSDimitry Andric void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc,
27668d75effSDimitry Andric                     FreeFunction freeFunc, AllocFunction allocFunc,
27768d75effSDimitry Andric                     HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, size_t dwBytes) {
27868d75effSDimitry Andric   CHECK(reallocFunc && heapSizeFunc && freeFunc && allocFunc);
27968d75effSDimitry Andric   GET_STACK_TRACE_MALLOC;
28068d75effSDimitry Andric   GET_CURRENT_PC_BP_SP;
28168d75effSDimitry Andric   (void)sp;
28268d75effSDimitry Andric   if (flags()->windows_hook_rtl_allocators) {
28368d75effSDimitry Andric     enum AllocationOwnership { NEITHER = 0, ASAN = 1, RTL = 2 };
28468d75effSDimitry Andric     AllocationOwnership ownershipState;
28568d75effSDimitry Andric     bool owned_rtlalloc = false;
28668d75effSDimitry Andric     bool owned_asan = __sanitizer_get_ownership(lpMem);
28768d75effSDimitry Andric 
28868d75effSDimitry Andric     if (!owned_asan)
28968d75effSDimitry Andric       owned_rtlalloc = HeapValidate(hHeap, 0, lpMem);
29068d75effSDimitry Andric 
29168d75effSDimitry Andric     if (owned_asan && !owned_rtlalloc)
29268d75effSDimitry Andric       ownershipState = ASAN;
29368d75effSDimitry Andric     else if (!owned_asan && owned_rtlalloc)
29468d75effSDimitry Andric       ownershipState = RTL;
29568d75effSDimitry Andric     else if (!owned_asan && !owned_rtlalloc)
29668d75effSDimitry Andric       ownershipState = NEITHER;
29768d75effSDimitry Andric 
29868d75effSDimitry Andric     // If this heap block which was allocated before the ASAN
29968d75effSDimitry Andric     // runtime came up, use the real HeapFree function.
300*5f757f3fSDimitry Andric     if (UNLIKELY(!AsanInited())) {
30168d75effSDimitry Andric       return reallocFunc(hHeap, dwFlags, lpMem, dwBytes);
30268d75effSDimitry Andric     }
30368d75effSDimitry Andric     bool only_asan_supported_flags =
30468d75effSDimitry Andric         (HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) == 0;
30568d75effSDimitry Andric 
30668d75effSDimitry Andric     if (ownershipState == RTL ||
30768d75effSDimitry Andric         (ownershipState == NEITHER && !only_asan_supported_flags)) {
30868d75effSDimitry Andric       if (only_asan_supported_flags) {
30968d75effSDimitry Andric         // if this is a conversion to ASAN upported flags, transfer this
31068d75effSDimitry Andric         // allocation to the ASAN allocator
31168d75effSDimitry Andric         void *replacement_alloc;
31268d75effSDimitry Andric         if (dwFlags & HEAP_ZERO_MEMORY)
31368d75effSDimitry Andric           replacement_alloc = asan_calloc(1, dwBytes, &stack);
31468d75effSDimitry Andric         else
31568d75effSDimitry Andric           replacement_alloc = asan_malloc(dwBytes, &stack);
31668d75effSDimitry Andric         if (replacement_alloc) {
31768d75effSDimitry Andric           size_t old_size = heapSizeFunc(hHeap, dwFlags, lpMem);
31868d75effSDimitry Andric           if (old_size == ((size_t)0) - 1) {
31968d75effSDimitry Andric             asan_free(replacement_alloc, &stack, FROM_MALLOC);
32068d75effSDimitry Andric             return nullptr;
32168d75effSDimitry Andric           }
32268d75effSDimitry Andric           REAL(memcpy)(replacement_alloc, lpMem, old_size);
32368d75effSDimitry Andric           freeFunc(hHeap, dwFlags, lpMem);
32468d75effSDimitry Andric         }
32568d75effSDimitry Andric         return replacement_alloc;
32668d75effSDimitry Andric       } else {
32768d75effSDimitry Andric         // owned by rtl or neither with unsupported ASAN flags,
32868d75effSDimitry Andric         // just pass back to original allocator
32968d75effSDimitry Andric         CHECK(ownershipState == RTL || ownershipState == NEITHER);
33068d75effSDimitry Andric         CHECK(!only_asan_supported_flags);
33168d75effSDimitry Andric         return reallocFunc(hHeap, dwFlags, lpMem, dwBytes);
33268d75effSDimitry Andric       }
33368d75effSDimitry Andric     }
33468d75effSDimitry Andric 
33568d75effSDimitry Andric     if (ownershipState == ASAN && !only_asan_supported_flags) {
33668d75effSDimitry Andric       // Conversion to unsupported flags allocation,
33768d75effSDimitry Andric       // transfer this allocation back to the original allocator.
33868d75effSDimitry Andric       void *replacement_alloc = allocFunc(hHeap, dwFlags, dwBytes);
33968d75effSDimitry Andric       size_t old_usable_size = 0;
34068d75effSDimitry Andric       if (replacement_alloc) {
34168d75effSDimitry Andric         old_usable_size = asan_malloc_usable_size(lpMem, pc, bp);
34268d75effSDimitry Andric         REAL(memcpy)(replacement_alloc, lpMem,
34368d75effSDimitry Andric                      Min<size_t>(dwBytes, old_usable_size));
34468d75effSDimitry Andric         asan_free(lpMem, &stack, FROM_MALLOC);
34568d75effSDimitry Andric       }
34668d75effSDimitry Andric       return replacement_alloc;
34768d75effSDimitry Andric     }
34868d75effSDimitry Andric 
34968d75effSDimitry Andric     CHECK((ownershipState == ASAN || ownershipState == NEITHER) &&
35068d75effSDimitry Andric           only_asan_supported_flags);
35168d75effSDimitry Andric     // At this point we should either be ASAN owned with ASAN supported flags
35268d75effSDimitry Andric     // or we owned by neither and have supported flags.
35368d75effSDimitry Andric     // Pass through even when it's neither since this could be a null realloc or
35468d75effSDimitry Andric     // UAF that ASAN needs to catch.
35568d75effSDimitry Andric   } else {
35668d75effSDimitry Andric     CHECK((HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) != 0 &&
35768d75effSDimitry Andric           "unsupported flags");
35868d75effSDimitry Andric   }
35968d75effSDimitry Andric   // asan_realloc will never reallocate in place, so for now this flag is
36068d75effSDimitry Andric   // unsupported until we figure out a way to fake this.
36168d75effSDimitry Andric   if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY)
36268d75effSDimitry Andric     return nullptr;
36368d75effSDimitry Andric 
36468d75effSDimitry Andric   // HeapReAlloc and HeapAlloc both happily accept 0 sized allocations.
36568d75effSDimitry Andric   // passing a 0 size into asan_realloc will free the allocation.
36668d75effSDimitry Andric   // To avoid this and keep behavior consistent, fudge the size if 0.
36768d75effSDimitry Andric   // (asan_malloc already does this)
36868d75effSDimitry Andric   if (dwBytes == 0)
36968d75effSDimitry Andric     dwBytes = 1;
37068d75effSDimitry Andric 
37168d75effSDimitry Andric   size_t old_size;
37268d75effSDimitry Andric   if (dwFlags & HEAP_ZERO_MEMORY)
37368d75effSDimitry Andric     old_size = asan_malloc_usable_size(lpMem, pc, bp);
37468d75effSDimitry Andric 
37568d75effSDimitry Andric   void *ptr = asan_realloc(lpMem, dwBytes, &stack);
37668d75effSDimitry Andric   if (ptr == nullptr)
37768d75effSDimitry Andric     return nullptr;
37868d75effSDimitry Andric 
37968d75effSDimitry Andric   if (dwFlags & HEAP_ZERO_MEMORY) {
38068d75effSDimitry Andric     size_t new_size = asan_malloc_usable_size(ptr, pc, bp);
38168d75effSDimitry Andric     if (old_size < new_size)
38268d75effSDimitry Andric       REAL(memset)(((u8 *)ptr) + old_size, 0, new_size - old_size);
38368d75effSDimitry Andric   }
38468d75effSDimitry Andric 
38568d75effSDimitry Andric   return ptr;
38668d75effSDimitry Andric }
38768d75effSDimitry Andric }  // namespace __asan
38868d75effSDimitry Andric 
38968d75effSDimitry Andric INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags,
39068d75effSDimitry Andric                    LPVOID lpMem, size_t dwBytes) {
39168d75effSDimitry Andric   return SharedReAlloc(REAL(HeapReAlloc), (SizeFunction)REAL(HeapSize),
39268d75effSDimitry Andric                        REAL(HeapFree), REAL(HeapAlloc), hHeap, dwFlags, lpMem,
39368d75effSDimitry Andric                        dwBytes);
39468d75effSDimitry Andric }
39568d75effSDimitry Andric 
39668d75effSDimitry Andric // The following functions are undocumented and subject to change.
39768d75effSDimitry Andric // However, hooking them is necessary to hook Windows heap
39868d75effSDimitry Andric // allocations with detours and their definitions are unlikely to change.
39968d75effSDimitry Andric // Comments in /minkernel/ntos/rtl/heappublic.c indicate that these functions
40068d75effSDimitry Andric // are part of the heap's public interface.
40168d75effSDimitry Andric typedef unsigned long LOGICAL;
40268d75effSDimitry Andric 
40368d75effSDimitry Andric // This function is documented as part of the Driver Development Kit but *not*
40468d75effSDimitry Andric // the Windows Development Kit.
40568d75effSDimitry Andric LOGICAL RtlFreeHeap(void* HeapHandle, DWORD Flags,
40668d75effSDimitry Andric                             void* BaseAddress);
40768d75effSDimitry Andric 
40868d75effSDimitry Andric // This function is documented as part of the Driver Development Kit but *not*
40968d75effSDimitry Andric // the Windows Development Kit.
41068d75effSDimitry Andric void* RtlAllocateHeap(void* HeapHandle, DWORD Flags, size_t Size);
41168d75effSDimitry Andric 
41268d75effSDimitry Andric // This function is completely undocumented.
41368d75effSDimitry Andric void*
41468d75effSDimitry Andric RtlReAllocateHeap(void* HeapHandle, DWORD Flags, void* BaseAddress,
41568d75effSDimitry Andric                   size_t Size);
41668d75effSDimitry Andric 
41768d75effSDimitry Andric // This function is completely undocumented.
41868d75effSDimitry Andric size_t RtlSizeHeap(void* HeapHandle, DWORD Flags, void* BaseAddress);
41968d75effSDimitry Andric 
42068d75effSDimitry Andric INTERCEPTOR_WINAPI(size_t, RtlSizeHeap, HANDLE HeapHandle, DWORD Flags,
42168d75effSDimitry Andric                    void* BaseAddress) {
42268d75effSDimitry Andric   if (!flags()->windows_hook_rtl_allocators ||
423*5f757f3fSDimitry Andric       UNLIKELY(!AsanInited() || OWNED_BY_RTL(HeapHandle, BaseAddress))) {
42468d75effSDimitry Andric     return REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress);
42568d75effSDimitry Andric   }
42668d75effSDimitry Andric   GET_CURRENT_PC_BP_SP;
42768d75effSDimitry Andric   (void)sp;
42868d75effSDimitry Andric   return asan_malloc_usable_size(BaseAddress, pc, bp);
42968d75effSDimitry Andric }
43068d75effSDimitry Andric 
43168d75effSDimitry Andric INTERCEPTOR_WINAPI(BOOL, RtlFreeHeap, HANDLE HeapHandle, DWORD Flags,
43268d75effSDimitry Andric                    void* BaseAddress) {
43368d75effSDimitry Andric   // Heap allocations happen before this function is hooked, so we must fall
43468d75effSDimitry Andric   // back to the original function if the pointer is not from the ASAN heap, or
43568d75effSDimitry Andric   // unsupported flags are provided.
43668d75effSDimitry Andric   if (!flags()->windows_hook_rtl_allocators ||
43768d75effSDimitry Andric       UNLIKELY((HEAP_FREE_UNSUPPORTED_FLAGS & Flags) != 0 ||
43868d75effSDimitry Andric                OWNED_BY_RTL(HeapHandle, BaseAddress))) {
43968d75effSDimitry Andric     return REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress);
44068d75effSDimitry Andric   }
44168d75effSDimitry Andric   GET_STACK_TRACE_FREE;
44268d75effSDimitry Andric   asan_free(BaseAddress, &stack, FROM_MALLOC);
44368d75effSDimitry Andric   return true;
44468d75effSDimitry Andric }
44568d75effSDimitry Andric 
44668d75effSDimitry Andric INTERCEPTOR_WINAPI(void*, RtlAllocateHeap, HANDLE HeapHandle, DWORD Flags,
44768d75effSDimitry Andric                    size_t Size) {
44868d75effSDimitry Andric   // If the ASAN runtime is not initialized, or we encounter an unsupported
44968d75effSDimitry Andric   // flag, fall back to the original allocator.
45068d75effSDimitry Andric   if (!flags()->windows_hook_rtl_allocators ||
451*5f757f3fSDimitry Andric       UNLIKELY(!AsanInited() ||
45268d75effSDimitry Andric                (Flags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) {
45368d75effSDimitry Andric     return REAL(RtlAllocateHeap)(HeapHandle, Flags, Size);
45468d75effSDimitry Andric   }
45568d75effSDimitry Andric   GET_STACK_TRACE_MALLOC;
45668d75effSDimitry Andric   void *p;
45768d75effSDimitry Andric   // Reading MSDN suggests that the *entire* usable allocation is zeroed out.
45868d75effSDimitry Andric   // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY.
45968d75effSDimitry Andric   // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083
46068d75effSDimitry Andric   if (Flags & HEAP_ZERO_MEMORY) {
46168d75effSDimitry Andric     p = asan_calloc(Size, 1, &stack);
46268d75effSDimitry Andric   } else {
46368d75effSDimitry Andric     p = asan_malloc(Size, &stack);
46468d75effSDimitry Andric   }
46568d75effSDimitry Andric   return p;
46668d75effSDimitry Andric }
46768d75effSDimitry Andric 
46868d75effSDimitry Andric INTERCEPTOR_WINAPI(void*, RtlReAllocateHeap, HANDLE HeapHandle, DWORD Flags,
46968d75effSDimitry Andric                    void* BaseAddress, size_t Size) {
47068d75effSDimitry Andric   // If it's actually a heap block which was allocated before the ASAN runtime
47168d75effSDimitry Andric   // came up, use the real RtlFreeHeap function.
47268d75effSDimitry Andric   if (!flags()->windows_hook_rtl_allocators)
47368d75effSDimitry Andric     return REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size);
47468d75effSDimitry Andric 
47568d75effSDimitry Andric   return SharedReAlloc(REAL(RtlReAllocateHeap), REAL(RtlSizeHeap),
47668d75effSDimitry Andric                        REAL(RtlFreeHeap), REAL(RtlAllocateHeap), HeapHandle,
47768d75effSDimitry Andric                        Flags, BaseAddress, Size);
47868d75effSDimitry Andric }
47968d75effSDimitry Andric 
48068d75effSDimitry Andric namespace __asan {
48168d75effSDimitry Andric 
48268d75effSDimitry Andric static void TryToOverrideFunction(const char *fname, uptr new_func) {
48368d75effSDimitry Andric   // Failure here is not fatal. The CRT may not be present, and different CRT
48468d75effSDimitry Andric   // versions use different symbols.
48568d75effSDimitry Andric   if (!__interception::OverrideFunction(fname, new_func))
48668d75effSDimitry Andric     VPrintf(2, "Failed to override function %s\n", fname);
48768d75effSDimitry Andric }
48868d75effSDimitry Andric 
48968d75effSDimitry Andric void ReplaceSystemMalloc() {
49068d75effSDimitry Andric #if defined(ASAN_DYNAMIC)
49168d75effSDimitry Andric   TryToOverrideFunction("free", (uptr)free);
49268d75effSDimitry Andric   TryToOverrideFunction("_free_base", (uptr)free);
49368d75effSDimitry Andric   TryToOverrideFunction("malloc", (uptr)malloc);
49468d75effSDimitry Andric   TryToOverrideFunction("_malloc_base", (uptr)malloc);
49568d75effSDimitry Andric   TryToOverrideFunction("_malloc_crt", (uptr)malloc);
49668d75effSDimitry Andric   TryToOverrideFunction("calloc", (uptr)calloc);
49768d75effSDimitry Andric   TryToOverrideFunction("_calloc_base", (uptr)calloc);
49868d75effSDimitry Andric   TryToOverrideFunction("_calloc_crt", (uptr)calloc);
49968d75effSDimitry Andric   TryToOverrideFunction("realloc", (uptr)realloc);
50068d75effSDimitry Andric   TryToOverrideFunction("_realloc_base", (uptr)realloc);
50168d75effSDimitry Andric   TryToOverrideFunction("_realloc_crt", (uptr)realloc);
50268d75effSDimitry Andric   TryToOverrideFunction("_recalloc", (uptr)_recalloc);
50368d75effSDimitry Andric   TryToOverrideFunction("_recalloc_base", (uptr)_recalloc);
50468d75effSDimitry Andric   TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc);
50568d75effSDimitry Andric   TryToOverrideFunction("_msize", (uptr)_msize);
50668d75effSDimitry Andric   TryToOverrideFunction("_msize_base", (uptr)_msize);
50768d75effSDimitry Andric   TryToOverrideFunction("_expand", (uptr)_expand);
50868d75effSDimitry Andric   TryToOverrideFunction("_expand_base", (uptr)_expand);
50968d75effSDimitry Andric 
51068d75effSDimitry Andric   if (flags()->windows_hook_rtl_allocators) {
51106c3fb27SDimitry Andric     ASAN_INTERCEPT_FUNC(HeapSize);
51206c3fb27SDimitry Andric     ASAN_INTERCEPT_FUNC(HeapFree);
51306c3fb27SDimitry Andric     ASAN_INTERCEPT_FUNC(HeapReAlloc);
51406c3fb27SDimitry Andric     ASAN_INTERCEPT_FUNC(HeapAlloc);
51568d75effSDimitry Andric 
51668d75effSDimitry Andric     // Undocumented functions must be intercepted by name, not by symbol.
51768d75effSDimitry Andric     __interception::OverrideFunction("RtlSizeHeap", (uptr)WRAP(RtlSizeHeap),
51868d75effSDimitry Andric                                      (uptr *)&REAL(RtlSizeHeap));
51968d75effSDimitry Andric     __interception::OverrideFunction("RtlFreeHeap", (uptr)WRAP(RtlFreeHeap),
52068d75effSDimitry Andric                                      (uptr *)&REAL(RtlFreeHeap));
52168d75effSDimitry Andric     __interception::OverrideFunction("RtlReAllocateHeap",
52268d75effSDimitry Andric                                      (uptr)WRAP(RtlReAllocateHeap),
52368d75effSDimitry Andric                                      (uptr *)&REAL(RtlReAllocateHeap));
52468d75effSDimitry Andric     __interception::OverrideFunction("RtlAllocateHeap",
52568d75effSDimitry Andric                                      (uptr)WRAP(RtlAllocateHeap),
52668d75effSDimitry Andric                                      (uptr *)&REAL(RtlAllocateHeap));
52768d75effSDimitry Andric   } else {
52868d75effSDimitry Andric #define INTERCEPT_UCRT_FUNCTION(func)                                  \
52968d75effSDimitry Andric   if (!INTERCEPT_FUNCTION_DLLIMPORT(                                   \
53068d75effSDimitry Andric           "ucrtbase.dll", "api-ms-win-core-heap-l1-1-0.dll", func)) {  \
53168d75effSDimitry Andric     VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func); \
53268d75effSDimitry Andric   }
53368d75effSDimitry Andric     INTERCEPT_UCRT_FUNCTION(HeapAlloc);
53468d75effSDimitry Andric     INTERCEPT_UCRT_FUNCTION(HeapFree);
53568d75effSDimitry Andric     INTERCEPT_UCRT_FUNCTION(HeapReAlloc);
53668d75effSDimitry Andric     INTERCEPT_UCRT_FUNCTION(HeapSize);
53768d75effSDimitry Andric #undef INTERCEPT_UCRT_FUNCTION
53868d75effSDimitry Andric   }
53968d75effSDimitry Andric   // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which
54068d75effSDimitry Andric   // enable cross-module inlining. This means our _malloc_base hook won't catch
54168d75effSDimitry Andric   // all CRT allocations. This code here patches the import table of
54268d75effSDimitry Andric   // ucrtbase.dll so that all attempts to use the lower-level win32 heap
54368d75effSDimitry Andric   // allocation API will be directed to ASan's heap. We don't currently
54468d75effSDimitry Andric   // intercept all calls to HeapAlloc. If we did, we would have to check on
54568d75effSDimitry Andric   // HeapFree whether the pointer came from ASan of from the system.
54668d75effSDimitry Andric 
54768d75effSDimitry Andric #endif  // defined(ASAN_DYNAMIC)
54868d75effSDimitry Andric }
54968d75effSDimitry Andric }  // namespace __asan
55068d75effSDimitry Andric 
55168d75effSDimitry Andric #endif  // _WIN32
552