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