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