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