1 //===-- asan_malloc_win.cc ------------------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file is a part of AddressSanitizer, an address sanity checker. 11 // 12 // Windows-specific malloc interception. 13 //===----------------------------------------------------------------------===// 14 15 #include "sanitizer_common/sanitizer_platform.h" 16 #if SANITIZER_WINDOWS 17 #define WIN32_LEAN_AND_MEAN 18 #include <windows.h> 19 20 #include "asan_allocator.h" 21 #include "asan_interceptors.h" 22 #include "asan_internal.h" 23 #include "asan_stack.h" 24 #include "interception/interception.h" 25 26 #include <stddef.h> 27 28 using namespace __asan; // NOLINT 29 30 // MT: Simply defining functions with the same signature in *.obj 31 // files overrides the standard functions in the CRT. 32 // MD: Memory allocation functions are defined in the CRT .dll, 33 // so we have to intercept them before they are called for the first time. 34 35 #if ASAN_DYNAMIC 36 # define ALLOCATION_FUNCTION_ATTRIBUTE 37 #else 38 # define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE 39 #endif 40 41 extern "C" { 42 ALLOCATION_FUNCTION_ATTRIBUTE 43 void free(void *ptr) { 44 GET_STACK_TRACE_FREE; 45 return asan_free(ptr, &stack, FROM_MALLOC); 46 } 47 48 ALLOCATION_FUNCTION_ATTRIBUTE 49 void _free_dbg(void *ptr, int) { 50 free(ptr); 51 } 52 53 ALLOCATION_FUNCTION_ATTRIBUTE 54 void _free_base(void *ptr) { 55 free(ptr); 56 } 57 58 ALLOCATION_FUNCTION_ATTRIBUTE 59 void cfree(void *ptr) { 60 CHECK(!"cfree() should not be used on Windows"); 61 } 62 63 ALLOCATION_FUNCTION_ATTRIBUTE 64 void *malloc(size_t size) { 65 GET_STACK_TRACE_MALLOC; 66 return asan_malloc(size, &stack); 67 } 68 69 ALLOCATION_FUNCTION_ATTRIBUTE 70 void *_malloc_base(size_t size) { 71 return malloc(size); 72 } 73 74 ALLOCATION_FUNCTION_ATTRIBUTE 75 void *_malloc_dbg(size_t size, int, const char *, int) { 76 return malloc(size); 77 } 78 79 ALLOCATION_FUNCTION_ATTRIBUTE 80 void *calloc(size_t nmemb, size_t size) { 81 GET_STACK_TRACE_MALLOC; 82 return asan_calloc(nmemb, size, &stack); 83 } 84 85 ALLOCATION_FUNCTION_ATTRIBUTE 86 void *_calloc_base(size_t nmemb, size_t size) { 87 return calloc(nmemb, size); 88 } 89 90 ALLOCATION_FUNCTION_ATTRIBUTE 91 void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) { 92 return calloc(nmemb, size); 93 } 94 95 ALLOCATION_FUNCTION_ATTRIBUTE 96 void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { 97 return calloc(nmemb, size); 98 } 99 100 ALLOCATION_FUNCTION_ATTRIBUTE 101 void *realloc(void *ptr, size_t size) { 102 GET_STACK_TRACE_MALLOC; 103 return asan_realloc(ptr, size, &stack); 104 } 105 106 ALLOCATION_FUNCTION_ATTRIBUTE 107 void *_realloc_dbg(void *ptr, size_t size, int) { 108 CHECK(!"_realloc_dbg should not exist!"); 109 return 0; 110 } 111 112 ALLOCATION_FUNCTION_ATTRIBUTE 113 void *_realloc_base(void *ptr, size_t size) { 114 return realloc(ptr, size); 115 } 116 117 ALLOCATION_FUNCTION_ATTRIBUTE 118 void *_recalloc(void *p, size_t n, size_t elem_size) { 119 if (!p) 120 return calloc(n, elem_size); 121 const size_t size = n * elem_size; 122 if (elem_size != 0 && size / elem_size != n) 123 return 0; 124 return realloc(p, size); 125 } 126 127 ALLOCATION_FUNCTION_ATTRIBUTE 128 size_t _msize(const void *ptr) { 129 GET_CURRENT_PC_BP_SP; 130 (void)sp; 131 return asan_malloc_usable_size(ptr, pc, bp); 132 } 133 134 ALLOCATION_FUNCTION_ATTRIBUTE 135 void *_expand(void *memblock, size_t size) { 136 // _expand is used in realloc-like functions to resize the buffer if possible. 137 // We don't want memory to stand still while resizing buffers, so return 0. 138 return 0; 139 } 140 141 ALLOCATION_FUNCTION_ATTRIBUTE 142 void *_expand_dbg(void *memblock, size_t size) { 143 return _expand(memblock, size); 144 } 145 146 // TODO(timurrrr): Might want to add support for _aligned_* allocation 147 // functions to detect a bit more bugs. Those functions seem to wrap malloc(). 148 149 int _CrtDbgReport(int, const char*, int, 150 const char*, const char*, ...) { 151 ShowStatsAndAbort(); 152 } 153 154 int _CrtDbgReportW(int reportType, const wchar_t*, int, 155 const wchar_t*, const wchar_t*, ...) { 156 ShowStatsAndAbort(); 157 } 158 159 int _CrtSetReportMode(int, int) { 160 return 0; 161 } 162 } // extern "C" 163 164 INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags, 165 SIZE_T dwBytes) { 166 GET_STACK_TRACE_MALLOC; 167 void *p = asan_malloc(dwBytes, &stack); 168 // Reading MSDN suggests that the *entire* usable allocation is zeroed out. 169 // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. 170 // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 171 if (dwFlags == HEAP_ZERO_MEMORY) 172 internal_memset(p, 0, asan_mz_size(p)); 173 else 174 CHECK(dwFlags == 0 && "unsupported heap flags"); 175 return p; 176 } 177 178 INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) { 179 CHECK(dwFlags == 0 && "unsupported heap flags"); 180 GET_STACK_TRACE_FREE; 181 asan_free(lpMem, &stack, FROM_MALLOC); 182 return true; 183 } 184 185 INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags, 186 LPVOID lpMem, SIZE_T dwBytes) { 187 GET_STACK_TRACE_MALLOC; 188 // Realloc should never reallocate in place. 189 if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) 190 return nullptr; 191 CHECK(dwFlags == 0 && "unsupported heap flags"); 192 return asan_realloc(lpMem, dwBytes, &stack); 193 } 194 195 INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags, 196 LPCVOID lpMem) { 197 CHECK(dwFlags == 0 && "unsupported heap flags"); 198 GET_CURRENT_PC_BP_SP; 199 (void)sp; 200 return asan_malloc_usable_size(lpMem, pc, bp); 201 } 202 203 namespace __asan { 204 205 static void TryToOverrideFunction(const char *fname, uptr new_func) { 206 // Failure here is not fatal. The CRT may not be present, and different CRT 207 // versions use different symbols. 208 if (!__interception::OverrideFunction(fname, new_func)) 209 VPrintf(2, "Failed to override function %s\n", fname); 210 } 211 212 void ReplaceSystemMalloc() { 213 #if defined(ASAN_DYNAMIC) 214 TryToOverrideFunction("free", (uptr)free); 215 TryToOverrideFunction("_free_base", (uptr)free); 216 TryToOverrideFunction("malloc", (uptr)malloc); 217 TryToOverrideFunction("_malloc_base", (uptr)malloc); 218 TryToOverrideFunction("_malloc_crt", (uptr)malloc); 219 TryToOverrideFunction("calloc", (uptr)calloc); 220 TryToOverrideFunction("_calloc_base", (uptr)calloc); 221 TryToOverrideFunction("_calloc_crt", (uptr)calloc); 222 TryToOverrideFunction("realloc", (uptr)realloc); 223 TryToOverrideFunction("_realloc_base", (uptr)realloc); 224 TryToOverrideFunction("_realloc_crt", (uptr)realloc); 225 TryToOverrideFunction("_recalloc", (uptr)_recalloc); 226 TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc); 227 TryToOverrideFunction("_msize", (uptr)_msize); 228 TryToOverrideFunction("_expand", (uptr)_expand); 229 TryToOverrideFunction("_expand_base", (uptr)_expand); 230 231 // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which 232 // enable cross-module inlining. This means our _malloc_base hook won't catch 233 // all CRT allocations. This code here patches the import table of 234 // ucrtbase.dll so that all attempts to use the lower-level win32 heap 235 // allocation API will be directed to ASan's heap. We don't currently 236 // intercept all calls to HeapAlloc. If we did, we would have to check on 237 // HeapFree whether the pointer came from ASan of from the system. 238 #define INTERCEPT_UCRT_FUNCTION(func) \ 239 if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll", \ 240 "api-ms-win-core-heap-l1-1-0.dll", func)) \ 241 VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func); 242 INTERCEPT_UCRT_FUNCTION(HeapAlloc); 243 INTERCEPT_UCRT_FUNCTION(HeapFree); 244 INTERCEPT_UCRT_FUNCTION(HeapReAlloc); 245 INTERCEPT_UCRT_FUNCTION(HeapSize); 246 #undef INTERCEPT_UCRT_FUNCTION 247 #endif 248 } 249 } // namespace __asan 250 251 #endif // _WIN32 252