1353944Sdim//===-- asan_malloc_win.cpp -----------------------------------------------===//
2353944Sdim//
3353944Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353944Sdim// See https://llvm.org/LICENSE.txt for license information.
5353944Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353944Sdim//
7353944Sdim//===----------------------------------------------------------------------===//
8353944Sdim//
9353944Sdim// This file is a part of AddressSanitizer, an address sanity checker.
10353944Sdim//
11353944Sdim// Windows-specific malloc interception.
12353944Sdim//===----------------------------------------------------------------------===//
13353944Sdim
14353944Sdim#include "sanitizer_common/sanitizer_allocator_interface.h"
15353944Sdim#include "sanitizer_common/sanitizer_platform.h"
16353944Sdim#if SANITIZER_WINDOWS
17353944Sdim#include "asan_allocator.h"
18353944Sdim#include "asan_interceptors.h"
19353944Sdim#include "asan_internal.h"
20353944Sdim#include "asan_stack.h"
21353944Sdim#include "interception/interception.h"
22353944Sdim#include <stddef.h>
23353944Sdim
24353944Sdim// Intentionally not including windows.h here, to avoid the risk of
25353944Sdim// pulling in conflicting declarations of these functions. (With mingw-w64,
26353944Sdim// there's a risk of windows.h pulling in stdint.h.)
27353944Sdimtypedef int BOOL;
28353944Sdimtypedef void *HANDLE;
29353944Sdimtypedef const void *LPCVOID;
30353944Sdimtypedef void *LPVOID;
31353944Sdim
32353944Sdimtypedef unsigned long DWORD;
33353944Sdimconstexpr unsigned long HEAP_ZERO_MEMORY = 0x00000008;
34353944Sdimconstexpr unsigned long HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010;
35353944Sdimconstexpr unsigned long HEAP_ALLOCATE_SUPPORTED_FLAGS = (HEAP_ZERO_MEMORY);
36353944Sdimconstexpr unsigned long HEAP_ALLOCATE_UNSUPPORTED_FLAGS =
37353944Sdim    (~HEAP_ALLOCATE_SUPPORTED_FLAGS);
38353944Sdimconstexpr unsigned long HEAP_FREE_UNSUPPORTED_FLAGS =
39353944Sdim    (~HEAP_ALLOCATE_SUPPORTED_FLAGS);
40353944Sdimconstexpr unsigned long HEAP_REALLOC_UNSUPPORTED_FLAGS =
41353944Sdim    (~HEAP_ALLOCATE_SUPPORTED_FLAGS);
42353944Sdim
43353944Sdim
44353944Sdimextern "C" {
45353944SdimLPVOID WINAPI HeapAlloc(HANDLE hHeap, DWORD dwFlags, size_t dwBytes);
46353944SdimLPVOID WINAPI HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem,
47353944Sdim                         size_t dwBytes);
48353944SdimBOOL WINAPI HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
49353944Sdimsize_t WINAPI HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
50353944Sdim
51353944SdimBOOL WINAPI HeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
52353944Sdim}
53353944Sdim
54353944Sdimusing namespace __asan;
55353944Sdim
56353944Sdim// MT: Simply defining functions with the same signature in *.obj
57353944Sdim// files overrides the standard functions in the CRT.
58353944Sdim// MD: Memory allocation functions are defined in the CRT .dll,
59353944Sdim// so we have to intercept them before they are called for the first time.
60353944Sdim
61353944Sdim#if ASAN_DYNAMIC
62353944Sdim# define ALLOCATION_FUNCTION_ATTRIBUTE
63353944Sdim#else
64353944Sdim# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
65353944Sdim#endif
66353944Sdim
67353944Sdimextern "C" {
68353944SdimALLOCATION_FUNCTION_ATTRIBUTE
69353944Sdimsize_t _msize(void *ptr) {
70353944Sdim  GET_CURRENT_PC_BP_SP;
71353944Sdim  (void)sp;
72353944Sdim  return asan_malloc_usable_size(ptr, pc, bp);
73353944Sdim}
74353944Sdim
75353944SdimALLOCATION_FUNCTION_ATTRIBUTE
76353944Sdimsize_t _msize_base(void *ptr) {
77353944Sdim  return _msize(ptr);
78353944Sdim}
79353944Sdim
80353944SdimALLOCATION_FUNCTION_ATTRIBUTE
81353944Sdimvoid free(void *ptr) {
82353944Sdim  GET_STACK_TRACE_FREE;
83353944Sdim  return asan_free(ptr, &stack, FROM_MALLOC);
84353944Sdim}
85353944Sdim
86353944SdimALLOCATION_FUNCTION_ATTRIBUTE
87353944Sdimvoid _free_dbg(void *ptr, int) {
88353944Sdim  free(ptr);
89353944Sdim}
90353944Sdim
91353944SdimALLOCATION_FUNCTION_ATTRIBUTE
92353944Sdimvoid _free_base(void *ptr) {
93353944Sdim  free(ptr);
94353944Sdim}
95353944Sdim
96353944SdimALLOCATION_FUNCTION_ATTRIBUTE
97353944Sdimvoid *malloc(size_t size) {
98353944Sdim  GET_STACK_TRACE_MALLOC;
99353944Sdim  return asan_malloc(size, &stack);
100353944Sdim}
101353944Sdim
102353944SdimALLOCATION_FUNCTION_ATTRIBUTE
103353944Sdimvoid *_malloc_base(size_t size) {
104353944Sdim  return malloc(size);
105353944Sdim}
106353944Sdim
107353944SdimALLOCATION_FUNCTION_ATTRIBUTE
108353944Sdimvoid *_malloc_dbg(size_t size, int, const char *, int) {
109353944Sdim  return malloc(size);
110353944Sdim}
111353944Sdim
112353944SdimALLOCATION_FUNCTION_ATTRIBUTE
113353944Sdimvoid *calloc(size_t nmemb, size_t size) {
114353944Sdim  GET_STACK_TRACE_MALLOC;
115353944Sdim  return asan_calloc(nmemb, size, &stack);
116353944Sdim}
117353944Sdim
118353944SdimALLOCATION_FUNCTION_ATTRIBUTE
119353944Sdimvoid *_calloc_base(size_t nmemb, size_t size) {
120353944Sdim  return calloc(nmemb, size);
121353944Sdim}
122353944Sdim
123353944SdimALLOCATION_FUNCTION_ATTRIBUTE
124353944Sdimvoid *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
125353944Sdim  return calloc(nmemb, size);
126353944Sdim}
127353944Sdim
128353944SdimALLOCATION_FUNCTION_ATTRIBUTE
129353944Sdimvoid *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
130353944Sdim  return calloc(nmemb, size);
131353944Sdim}
132353944Sdim
133353944SdimALLOCATION_FUNCTION_ATTRIBUTE
134353944Sdimvoid *realloc(void *ptr, size_t size) {
135353944Sdim  GET_STACK_TRACE_MALLOC;
136353944Sdim  return asan_realloc(ptr, size, &stack);
137353944Sdim}
138353944Sdim
139353944SdimALLOCATION_FUNCTION_ATTRIBUTE
140353944Sdimvoid *_realloc_dbg(void *ptr, size_t size, int) {
141353944Sdim  UNREACHABLE("_realloc_dbg should not exist!");
142353944Sdim  return 0;
143353944Sdim}
144353944Sdim
145353944SdimALLOCATION_FUNCTION_ATTRIBUTE
146353944Sdimvoid *_realloc_base(void *ptr, size_t size) {
147353944Sdim  return realloc(ptr, size);
148353944Sdim}
149353944Sdim
150353944SdimALLOCATION_FUNCTION_ATTRIBUTE
151353944Sdimvoid *_recalloc(void *p, size_t n, size_t elem_size) {
152353944Sdim  if (!p)
153353944Sdim    return calloc(n, elem_size);
154353944Sdim  const size_t size = n * elem_size;
155353944Sdim  if (elem_size != 0 && size / elem_size != n)
156353944Sdim    return 0;
157353944Sdim
158353944Sdim  size_t old_size = _msize(p);
159353944Sdim  void *new_alloc = malloc(size);
160353944Sdim  if (new_alloc) {
161353944Sdim    REAL(memcpy)(new_alloc, p, Min<size_t>(size, old_size));
162353944Sdim    if (old_size < size)
163353944Sdim      REAL(memset)(((u8 *)new_alloc) + old_size, 0, size - old_size);
164353944Sdim    free(p);
165353944Sdim  }
166353944Sdim  return new_alloc;
167353944Sdim}
168353944Sdim
169353944SdimALLOCATION_FUNCTION_ATTRIBUTE
170353944Sdimvoid *_recalloc_base(void *p, size_t n, size_t elem_size) {
171353944Sdim  return _recalloc(p, n, elem_size);
172353944Sdim}
173353944Sdim
174353944SdimALLOCATION_FUNCTION_ATTRIBUTE
175353944Sdimvoid *_expand(void *memblock, size_t size) {
176353944Sdim  // _expand is used in realloc-like functions to resize the buffer if possible.
177353944Sdim  // We don't want memory to stand still while resizing buffers, so return 0.
178353944Sdim  return 0;
179353944Sdim}
180353944Sdim
181353944SdimALLOCATION_FUNCTION_ATTRIBUTE
182353944Sdimvoid *_expand_dbg(void *memblock, size_t size) {
183353944Sdim  return _expand(memblock, size);
184353944Sdim}
185353944Sdim
186353944Sdim// TODO(timurrrr): Might want to add support for _aligned_* allocation
187353944Sdim// functions to detect a bit more bugs.  Those functions seem to wrap malloc().
188353944Sdim
189353944Sdimint _CrtDbgReport(int, const char*, int,
190353944Sdim                  const char*, const char*, ...) {
191353944Sdim  ShowStatsAndAbort();
192353944Sdim}
193353944Sdim
194353944Sdimint _CrtDbgReportW(int reportType, const wchar_t*, int,
195353944Sdim                   const wchar_t*, const wchar_t*, ...) {
196353944Sdim  ShowStatsAndAbort();
197353944Sdim}
198353944Sdim
199353944Sdimint _CrtSetReportMode(int, int) {
200353944Sdim  return 0;
201353944Sdim}
202353944Sdim}  // extern "C"
203353944Sdim
204353944Sdim#define OWNED_BY_RTL(heap, memory) \
205353944Sdim  (!__sanitizer_get_ownership(memory) && HeapValidate(heap, 0, memory))
206353944Sdim
207353944SdimINTERCEPTOR_WINAPI(size_t, HeapSize, HANDLE hHeap, DWORD dwFlags,
208353944Sdim                   LPCVOID lpMem) {
209353944Sdim  // If the RTL allocators are hooked we need to check whether the ASAN
210353944Sdim  // allocator owns the pointer we're about to use. Allocations occur before
211353944Sdim  // interception takes place, so if it is not owned by the RTL heap we can
212353944Sdim  // pass it to the ASAN heap for inspection.
213353944Sdim  if (flags()->windows_hook_rtl_allocators) {
214353944Sdim    if (!asan_inited || OWNED_BY_RTL(hHeap, lpMem))
215353944Sdim      return REAL(HeapSize)(hHeap, dwFlags, lpMem);
216353944Sdim  } else {
217353944Sdim    CHECK(dwFlags == 0 && "unsupported heap flags");
218353944Sdim  }
219353944Sdim  GET_CURRENT_PC_BP_SP;
220353944Sdim  (void)sp;
221353944Sdim  return asan_malloc_usable_size(lpMem, pc, bp);
222353944Sdim}
223353944Sdim
224353944SdimINTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags,
225353944Sdim                   size_t dwBytes) {
226353944Sdim  // If the ASAN runtime is not initialized, or we encounter an unsupported
227353944Sdim  // flag, fall back to the original allocator.
228353944Sdim  if (flags()->windows_hook_rtl_allocators) {
229353944Sdim    if (UNLIKELY(!asan_inited ||
230353944Sdim                 (dwFlags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) {
231353944Sdim      return REAL(HeapAlloc)(hHeap, dwFlags, dwBytes);
232353944Sdim    }
233353944Sdim  } else {
234353944Sdim    // In the case that we don't hook the rtl allocators,
235353944Sdim    // this becomes an assert since there is no failover to the original
236353944Sdim    // allocator.
237353944Sdim    CHECK((HEAP_ALLOCATE_UNSUPPORTED_FLAGS & dwFlags) != 0 &&
238353944Sdim          "unsupported flags");
239353944Sdim  }
240353944Sdim  GET_STACK_TRACE_MALLOC;
241353944Sdim  void *p = asan_malloc(dwBytes, &stack);
242353944Sdim  // Reading MSDN suggests that the *entire* usable allocation is zeroed out.
243353944Sdim  // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY.
244353944Sdim  // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083
245353944Sdim  if (p && (dwFlags & HEAP_ZERO_MEMORY)) {
246353944Sdim    GET_CURRENT_PC_BP_SP;
247353944Sdim    (void)sp;
248353944Sdim    auto usable_size = asan_malloc_usable_size(p, pc, bp);
249353944Sdim    internal_memset(p, 0, usable_size);
250353944Sdim  }
251353944Sdim  return p;
252353944Sdim}
253353944Sdim
254353944SdimINTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) {
255353944Sdim  // Heap allocations happen before this function is hooked, so we must fall
256353944Sdim  // back to the original function if the pointer is not from the ASAN heap,
257353944Sdim  // or unsupported flags are provided.
258353944Sdim  if (flags()->windows_hook_rtl_allocators) {
259353944Sdim    if (OWNED_BY_RTL(hHeap, lpMem))
260353944Sdim      return REAL(HeapFree)(hHeap, dwFlags, lpMem);
261353944Sdim  } else {
262353944Sdim    CHECK((HEAP_FREE_UNSUPPORTED_FLAGS & dwFlags) != 0 && "unsupported flags");
263353944Sdim  }
264353944Sdim  GET_STACK_TRACE_FREE;
265353944Sdim  asan_free(lpMem, &stack, FROM_MALLOC);
266353944Sdim  return true;
267353944Sdim}
268353944Sdim
269353944Sdimnamespace __asan {
270353944Sdimusing AllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, size_t);
271353944Sdimusing ReAllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, LPVOID, size_t);
272353944Sdimusing SizeFunction = size_t(WINAPI *)(HANDLE, DWORD, LPVOID);
273353944Sdimusing FreeFunction = BOOL(WINAPI *)(HANDLE, DWORD, LPVOID);
274353944Sdim
275353944Sdimvoid *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc,
276353944Sdim                    FreeFunction freeFunc, AllocFunction allocFunc,
277353944Sdim                    HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, size_t dwBytes) {
278353944Sdim  CHECK(reallocFunc && heapSizeFunc && freeFunc && allocFunc);
279353944Sdim  GET_STACK_TRACE_MALLOC;
280353944Sdim  GET_CURRENT_PC_BP_SP;
281353944Sdim  (void)sp;
282353944Sdim  if (flags()->windows_hook_rtl_allocators) {
283353944Sdim    enum AllocationOwnership { NEITHER = 0, ASAN = 1, RTL = 2 };
284353944Sdim    AllocationOwnership ownershipState;
285353944Sdim    bool owned_rtlalloc = false;
286353944Sdim    bool owned_asan = __sanitizer_get_ownership(lpMem);
287353944Sdim
288353944Sdim    if (!owned_asan)
289353944Sdim      owned_rtlalloc = HeapValidate(hHeap, 0, lpMem);
290353944Sdim
291353944Sdim    if (owned_asan && !owned_rtlalloc)
292353944Sdim      ownershipState = ASAN;
293353944Sdim    else if (!owned_asan && owned_rtlalloc)
294353944Sdim      ownershipState = RTL;
295353944Sdim    else if (!owned_asan && !owned_rtlalloc)
296353944Sdim      ownershipState = NEITHER;
297353944Sdim
298353944Sdim    // If this heap block which was allocated before the ASAN
299353944Sdim    // runtime came up, use the real HeapFree function.
300353944Sdim    if (UNLIKELY(!asan_inited)) {
301353944Sdim      return reallocFunc(hHeap, dwFlags, lpMem, dwBytes);
302353944Sdim    }
303353944Sdim    bool only_asan_supported_flags =
304353944Sdim        (HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) == 0;
305353944Sdim
306353944Sdim    if (ownershipState == RTL ||
307353944Sdim        (ownershipState == NEITHER && !only_asan_supported_flags)) {
308353944Sdim      if (only_asan_supported_flags) {
309353944Sdim        // if this is a conversion to ASAN upported flags, transfer this
310353944Sdim        // allocation to the ASAN allocator
311353944Sdim        void *replacement_alloc;
312353944Sdim        if (dwFlags & HEAP_ZERO_MEMORY)
313353944Sdim          replacement_alloc = asan_calloc(1, dwBytes, &stack);
314353944Sdim        else
315353944Sdim          replacement_alloc = asan_malloc(dwBytes, &stack);
316353944Sdim        if (replacement_alloc) {
317353944Sdim          size_t old_size = heapSizeFunc(hHeap, dwFlags, lpMem);
318353944Sdim          if (old_size == ((size_t)0) - 1) {
319353944Sdim            asan_free(replacement_alloc, &stack, FROM_MALLOC);
320353944Sdim            return nullptr;
321353944Sdim          }
322353944Sdim          REAL(memcpy)(replacement_alloc, lpMem, old_size);
323353944Sdim          freeFunc(hHeap, dwFlags, lpMem);
324353944Sdim        }
325353944Sdim        return replacement_alloc;
326353944Sdim      } else {
327353944Sdim        // owned by rtl or neither with unsupported ASAN flags,
328353944Sdim        // just pass back to original allocator
329353944Sdim        CHECK(ownershipState == RTL || ownershipState == NEITHER);
330353944Sdim        CHECK(!only_asan_supported_flags);
331353944Sdim        return reallocFunc(hHeap, dwFlags, lpMem, dwBytes);
332353944Sdim      }
333353944Sdim    }
334353944Sdim
335353944Sdim    if (ownershipState == ASAN && !only_asan_supported_flags) {
336353944Sdim      // Conversion to unsupported flags allocation,
337353944Sdim      // transfer this allocation back to the original allocator.
338353944Sdim      void *replacement_alloc = allocFunc(hHeap, dwFlags, dwBytes);
339353944Sdim      size_t old_usable_size = 0;
340353944Sdim      if (replacement_alloc) {
341353944Sdim        old_usable_size = asan_malloc_usable_size(lpMem, pc, bp);
342353944Sdim        REAL(memcpy)(replacement_alloc, lpMem,
343353944Sdim                     Min<size_t>(dwBytes, old_usable_size));
344353944Sdim        asan_free(lpMem, &stack, FROM_MALLOC);
345353944Sdim      }
346353944Sdim      return replacement_alloc;
347353944Sdim    }
348353944Sdim
349353944Sdim    CHECK((ownershipState == ASAN || ownershipState == NEITHER) &&
350353944Sdim          only_asan_supported_flags);
351353944Sdim    // At this point we should either be ASAN owned with ASAN supported flags
352353944Sdim    // or we owned by neither and have supported flags.
353353944Sdim    // Pass through even when it's neither since this could be a null realloc or
354353944Sdim    // UAF that ASAN needs to catch.
355353944Sdim  } else {
356353944Sdim    CHECK((HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) != 0 &&
357353944Sdim          "unsupported flags");
358353944Sdim  }
359353944Sdim  // asan_realloc will never reallocate in place, so for now this flag is
360353944Sdim  // unsupported until we figure out a way to fake this.
361353944Sdim  if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY)
362353944Sdim    return nullptr;
363353944Sdim
364353944Sdim  // HeapReAlloc and HeapAlloc both happily accept 0 sized allocations.
365353944Sdim  // passing a 0 size into asan_realloc will free the allocation.
366353944Sdim  // To avoid this and keep behavior consistent, fudge the size if 0.
367353944Sdim  // (asan_malloc already does this)
368353944Sdim  if (dwBytes == 0)
369353944Sdim    dwBytes = 1;
370353944Sdim
371353944Sdim  size_t old_size;
372353944Sdim  if (dwFlags & HEAP_ZERO_MEMORY)
373353944Sdim    old_size = asan_malloc_usable_size(lpMem, pc, bp);
374353944Sdim
375353944Sdim  void *ptr = asan_realloc(lpMem, dwBytes, &stack);
376353944Sdim  if (ptr == nullptr)
377353944Sdim    return nullptr;
378353944Sdim
379353944Sdim  if (dwFlags & HEAP_ZERO_MEMORY) {
380353944Sdim    size_t new_size = asan_malloc_usable_size(ptr, pc, bp);
381353944Sdim    if (old_size < new_size)
382353944Sdim      REAL(memset)(((u8 *)ptr) + old_size, 0, new_size - old_size);
383353944Sdim  }
384353944Sdim
385353944Sdim  return ptr;
386353944Sdim}
387353944Sdim}  // namespace __asan
388353944Sdim
389353944SdimINTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags,
390353944Sdim                   LPVOID lpMem, size_t dwBytes) {
391353944Sdim  return SharedReAlloc(REAL(HeapReAlloc), (SizeFunction)REAL(HeapSize),
392353944Sdim                       REAL(HeapFree), REAL(HeapAlloc), hHeap, dwFlags, lpMem,
393353944Sdim                       dwBytes);
394353944Sdim}
395353944Sdim
396353944Sdim// The following functions are undocumented and subject to change.
397353944Sdim// However, hooking them is necessary to hook Windows heap
398353944Sdim// allocations with detours and their definitions are unlikely to change.
399353944Sdim// Comments in /minkernel/ntos/rtl/heappublic.c indicate that these functions
400353944Sdim// are part of the heap's public interface.
401353944Sdimtypedef unsigned long LOGICAL;
402353944Sdim
403353944Sdim// This function is documented as part of the Driver Development Kit but *not*
404353944Sdim// the Windows Development Kit.
405353944SdimLOGICAL RtlFreeHeap(void* HeapHandle, DWORD Flags,
406353944Sdim                            void* BaseAddress);
407353944Sdim
408353944Sdim// This function is documented as part of the Driver Development Kit but *not*
409353944Sdim// the Windows Development Kit.
410353944Sdimvoid* RtlAllocateHeap(void* HeapHandle, DWORD Flags, size_t Size);
411353944Sdim
412353944Sdim// This function is completely undocumented.
413353944Sdimvoid*
414353944SdimRtlReAllocateHeap(void* HeapHandle, DWORD Flags, void* BaseAddress,
415353944Sdim                  size_t Size);
416353944Sdim
417353944Sdim// This function is completely undocumented.
418353944Sdimsize_t RtlSizeHeap(void* HeapHandle, DWORD Flags, void* BaseAddress);
419353944Sdim
420353944SdimINTERCEPTOR_WINAPI(size_t, RtlSizeHeap, HANDLE HeapHandle, DWORD Flags,
421353944Sdim                   void* BaseAddress) {
422353944Sdim  if (!flags()->windows_hook_rtl_allocators ||
423353944Sdim      UNLIKELY(!asan_inited || OWNED_BY_RTL(HeapHandle, BaseAddress))) {
424353944Sdim    return REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress);
425353944Sdim  }
426353944Sdim  GET_CURRENT_PC_BP_SP;
427353944Sdim  (void)sp;
428353944Sdim  return asan_malloc_usable_size(BaseAddress, pc, bp);
429353944Sdim}
430353944Sdim
431353944SdimINTERCEPTOR_WINAPI(BOOL, RtlFreeHeap, HANDLE HeapHandle, DWORD Flags,
432353944Sdim                   void* BaseAddress) {
433353944Sdim  // Heap allocations happen before this function is hooked, so we must fall
434353944Sdim  // back to the original function if the pointer is not from the ASAN heap, or
435353944Sdim  // unsupported flags are provided.
436353944Sdim  if (!flags()->windows_hook_rtl_allocators ||
437353944Sdim      UNLIKELY((HEAP_FREE_UNSUPPORTED_FLAGS & Flags) != 0 ||
438353944Sdim               OWNED_BY_RTL(HeapHandle, BaseAddress))) {
439353944Sdim    return REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress);
440353944Sdim  }
441353944Sdim  GET_STACK_TRACE_FREE;
442353944Sdim  asan_free(BaseAddress, &stack, FROM_MALLOC);
443353944Sdim  return true;
444353944Sdim}
445353944Sdim
446353944SdimINTERCEPTOR_WINAPI(void*, RtlAllocateHeap, HANDLE HeapHandle, DWORD Flags,
447353944Sdim                   size_t Size) {
448353944Sdim  // If the ASAN runtime is not initialized, or we encounter an unsupported
449353944Sdim  // flag, fall back to the original allocator.
450353944Sdim  if (!flags()->windows_hook_rtl_allocators ||
451353944Sdim      UNLIKELY(!asan_inited ||
452353944Sdim               (Flags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) {
453353944Sdim    return REAL(RtlAllocateHeap)(HeapHandle, Flags, Size);
454353944Sdim  }
455353944Sdim  GET_STACK_TRACE_MALLOC;
456353944Sdim  void *p;
457353944Sdim  // Reading MSDN suggests that the *entire* usable allocation is zeroed out.
458353944Sdim  // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY.
459353944Sdim  // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083
460353944Sdim  if (Flags & HEAP_ZERO_MEMORY) {
461353944Sdim    p = asan_calloc(Size, 1, &stack);
462353944Sdim  } else {
463353944Sdim    p = asan_malloc(Size, &stack);
464353944Sdim  }
465353944Sdim  return p;
466353944Sdim}
467353944Sdim
468353944SdimINTERCEPTOR_WINAPI(void*, RtlReAllocateHeap, HANDLE HeapHandle, DWORD Flags,
469353944Sdim                   void* BaseAddress, size_t Size) {
470353944Sdim  // If it's actually a heap block which was allocated before the ASAN runtime
471353944Sdim  // came up, use the real RtlFreeHeap function.
472353944Sdim  if (!flags()->windows_hook_rtl_allocators)
473353944Sdim    return REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size);
474353944Sdim
475353944Sdim  return SharedReAlloc(REAL(RtlReAllocateHeap), REAL(RtlSizeHeap),
476353944Sdim                       REAL(RtlFreeHeap), REAL(RtlAllocateHeap), HeapHandle,
477353944Sdim                       Flags, BaseAddress, Size);
478353944Sdim}
479353944Sdim
480353944Sdimnamespace __asan {
481353944Sdim
482353944Sdimstatic void TryToOverrideFunction(const char *fname, uptr new_func) {
483353944Sdim  // Failure here is not fatal. The CRT may not be present, and different CRT
484353944Sdim  // versions use different symbols.
485353944Sdim  if (!__interception::OverrideFunction(fname, new_func))
486353944Sdim    VPrintf(2, "Failed to override function %s\n", fname);
487353944Sdim}
488353944Sdim
489353944Sdimvoid ReplaceSystemMalloc() {
490353944Sdim#if defined(ASAN_DYNAMIC)
491353944Sdim  TryToOverrideFunction("free", (uptr)free);
492353944Sdim  TryToOverrideFunction("_free_base", (uptr)free);
493353944Sdim  TryToOverrideFunction("malloc", (uptr)malloc);
494353944Sdim  TryToOverrideFunction("_malloc_base", (uptr)malloc);
495353944Sdim  TryToOverrideFunction("_malloc_crt", (uptr)malloc);
496353944Sdim  TryToOverrideFunction("calloc", (uptr)calloc);
497353944Sdim  TryToOverrideFunction("_calloc_base", (uptr)calloc);
498353944Sdim  TryToOverrideFunction("_calloc_crt", (uptr)calloc);
499353944Sdim  TryToOverrideFunction("realloc", (uptr)realloc);
500353944Sdim  TryToOverrideFunction("_realloc_base", (uptr)realloc);
501353944Sdim  TryToOverrideFunction("_realloc_crt", (uptr)realloc);
502353944Sdim  TryToOverrideFunction("_recalloc", (uptr)_recalloc);
503353944Sdim  TryToOverrideFunction("_recalloc_base", (uptr)_recalloc);
504353944Sdim  TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc);
505353944Sdim  TryToOverrideFunction("_msize", (uptr)_msize);
506353944Sdim  TryToOverrideFunction("_msize_base", (uptr)_msize);
507353944Sdim  TryToOverrideFunction("_expand", (uptr)_expand);
508353944Sdim  TryToOverrideFunction("_expand_base", (uptr)_expand);
509353944Sdim
510353944Sdim  if (flags()->windows_hook_rtl_allocators) {
511353944Sdim    INTERCEPT_FUNCTION(HeapSize);
512353944Sdim    INTERCEPT_FUNCTION(HeapFree);
513353944Sdim    INTERCEPT_FUNCTION(HeapReAlloc);
514353944Sdim    INTERCEPT_FUNCTION(HeapAlloc);
515353944Sdim
516353944Sdim    // Undocumented functions must be intercepted by name, not by symbol.
517353944Sdim    __interception::OverrideFunction("RtlSizeHeap", (uptr)WRAP(RtlSizeHeap),
518353944Sdim                                     (uptr *)&REAL(RtlSizeHeap));
519353944Sdim    __interception::OverrideFunction("RtlFreeHeap", (uptr)WRAP(RtlFreeHeap),
520353944Sdim                                     (uptr *)&REAL(RtlFreeHeap));
521353944Sdim    __interception::OverrideFunction("RtlReAllocateHeap",
522353944Sdim                                     (uptr)WRAP(RtlReAllocateHeap),
523353944Sdim                                     (uptr *)&REAL(RtlReAllocateHeap));
524353944Sdim    __interception::OverrideFunction("RtlAllocateHeap",
525353944Sdim                                     (uptr)WRAP(RtlAllocateHeap),
526353944Sdim                                     (uptr *)&REAL(RtlAllocateHeap));
527353944Sdim  } else {
528353944Sdim#define INTERCEPT_UCRT_FUNCTION(func)                                  \
529353944Sdim  if (!INTERCEPT_FUNCTION_DLLIMPORT(                                   \
530353944Sdim          "ucrtbase.dll", "api-ms-win-core-heap-l1-1-0.dll", func)) {  \
531353944Sdim    VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func); \
532353944Sdim  }
533353944Sdim    INTERCEPT_UCRT_FUNCTION(HeapAlloc);
534353944Sdim    INTERCEPT_UCRT_FUNCTION(HeapFree);
535353944Sdim    INTERCEPT_UCRT_FUNCTION(HeapReAlloc);
536353944Sdim    INTERCEPT_UCRT_FUNCTION(HeapSize);
537353944Sdim#undef INTERCEPT_UCRT_FUNCTION
538353944Sdim  }
539353944Sdim  // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which
540353944Sdim  // enable cross-module inlining. This means our _malloc_base hook won't catch
541353944Sdim  // all CRT allocations. This code here patches the import table of
542353944Sdim  // ucrtbase.dll so that all attempts to use the lower-level win32 heap
543353944Sdim  // allocation API will be directed to ASan's heap. We don't currently
544353944Sdim  // intercept all calls to HeapAlloc. If we did, we would have to check on
545353944Sdim  // HeapFree whether the pointer came from ASan of from the system.
546353944Sdim
547353944Sdim#endif  // defined(ASAN_DYNAMIC)
548353944Sdim}
549353944Sdim}  // namespace __asan
550353944Sdim
551353944Sdim#endif  // _WIN32
552