1//===-- asan_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 details. 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 <stdlib.h> 21 22#include "asan_interceptors.h" 23#include "asan_internal.h" 24#include "asan_report.h" 25#include "asan_stack.h" 26#include "asan_thread.h" 27#include "asan_mapping.h" 28#include "sanitizer_common/sanitizer_libc.h" 29#include "sanitizer_common/sanitizer_mutex.h" 30 31using namespace __asan; // NOLINT 32 33extern "C" { 34SANITIZER_INTERFACE_ATTRIBUTE 35int __asan_should_detect_stack_use_after_return() { 36 __asan_init(); 37 return __asan_option_detect_stack_use_after_return; 38} 39 40// -------------------- A workaround for the abscence of weak symbols ----- {{{ 41// We don't have a direct equivalent of weak symbols when using MSVC, but we can 42// use the /alternatename directive to tell the linker to default a specific 43// symbol to a specific value, which works nicely for allocator hooks and 44// __asan_default_options(). 45void __sanitizer_default_malloc_hook(void *ptr, uptr size) { } 46void __sanitizer_default_free_hook(void *ptr) { } 47const char* __asan_default_default_options() { return ""; } 48const char* __asan_default_default_suppressions() { return ""; } 49void __asan_default_on_error() {} 50// 64-bit msvc will not prepend an underscore for symbols. 51#ifdef _WIN64 52#pragma comment(linker, "/alternatename:__sanitizer_malloc_hook=__sanitizer_default_malloc_hook") // NOLINT 53#pragma comment(linker, "/alternatename:__sanitizer_free_hook=__sanitizer_default_free_hook") // NOLINT 54#pragma comment(linker, "/alternatename:__asan_default_options=__asan_default_default_options") // NOLINT 55#pragma comment(linker, "/alternatename:__asan_default_suppressions=__asan_default_default_suppressions") // NOLINT 56#pragma comment(linker, "/alternatename:__asan_on_error=__asan_default_on_error") // NOLINT 57#else 58#pragma comment(linker, "/alternatename:___sanitizer_malloc_hook=___sanitizer_default_malloc_hook") // NOLINT 59#pragma comment(linker, "/alternatename:___sanitizer_free_hook=___sanitizer_default_free_hook") // NOLINT 60#pragma comment(linker, "/alternatename:___asan_default_options=___asan_default_default_options") // NOLINT 61#pragma comment(linker, "/alternatename:___asan_default_suppressions=___asan_default_default_suppressions") // NOLINT 62#pragma comment(linker, "/alternatename:___asan_on_error=___asan_default_on_error") // NOLINT 63#endif 64// }}} 65} // extern "C" 66 67// ---------------------- Windows-specific inteceptors ---------------- {{{ 68INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) { 69 CHECK(REAL(RaiseException)); 70 __asan_handle_no_return(); 71 REAL(RaiseException)(a, b, c, d); 72} 73 74// TODO(wwchrome): Win64 has no _except_handler3/4. 75// Need to implement _C_specific_handler instead. 76#ifndef _WIN64 77INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) { 78 CHECK(REAL(_except_handler3)); 79 __asan_handle_no_return(); 80 return REAL(_except_handler3)(a, b, c, d); 81} 82 83#if ASAN_DYNAMIC 84// This handler is named differently in -MT and -MD CRTs. 85#define _except_handler4 _except_handler4_common 86#endif 87INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { 88 CHECK(REAL(_except_handler4)); 89 __asan_handle_no_return(); 90 return REAL(_except_handler4)(a, b, c, d); 91} 92#endif 93 94static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { 95 AsanThread *t = (AsanThread*)arg; 96 SetCurrentThread(t); 97 return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr); 98} 99 100INTERCEPTOR_WINAPI(DWORD, CreateThread, 101 void* security, uptr stack_size, 102 DWORD (__stdcall *start_routine)(void*), void* arg, 103 DWORD thr_flags, void* tid) { 104 // Strict init-order checking is thread-hostile. 105 if (flags()->strict_init_order) 106 StopInitOrderChecking(); 107 GET_STACK_TRACE_THREAD; 108 // FIXME: The CreateThread interceptor is not the same as a pthread_create 109 // one. This is a bandaid fix for PR22025. 110 bool detached = false; // FIXME: how can we determine it on Windows? 111 u32 current_tid = GetCurrentTidOrInvalid(); 112 AsanThread *t = 113 AsanThread::Create(start_routine, arg, current_tid, &stack, detached); 114 return REAL(CreateThread)(security, stack_size, 115 asan_thread_start, t, thr_flags, tid); 116} 117 118namespace { 119BlockingMutex mu_for_thread_tracking(LINKER_INITIALIZED); 120 121void EnsureWorkerThreadRegistered() { 122 // FIXME: GetCurrentThread relies on TSD, which might not play well with 123 // system thread pools. We might want to use something like reference 124 // counting to zero out GetCurrentThread() underlying storage when the last 125 // work item finishes? Or can we disable reclaiming of threads in the pool? 126 BlockingMutexLock l(&mu_for_thread_tracking); 127 if (__asan::GetCurrentThread()) 128 return; 129 130 AsanThread *t = AsanThread::Create( 131 /* start_routine */ nullptr, /* arg */ nullptr, 132 /* parent_tid */ -1, /* stack */ nullptr, /* detached */ true); 133 t->Init(); 134 asanThreadRegistry().StartThread(t->tid(), 0, 0); 135 SetCurrentThread(t); 136} 137} // namespace 138 139INTERCEPTOR_WINAPI(DWORD, NtWaitForWorkViaWorkerFactory, DWORD a, DWORD b) { 140 // NtWaitForWorkViaWorkerFactory is called from system worker pool threads to 141 // query work scheduled by BindIoCompletionCallback, QueueUserWorkItem, etc. 142 // System worker pool threads are created at arbitraty point in time and 143 // without using CreateThread, so we wrap NtWaitForWorkViaWorkerFactory 144 // instead and don't register a specific parent_tid/stack. 145 EnsureWorkerThreadRegistered(); 146 return REAL(NtWaitForWorkViaWorkerFactory)(a, b); 147} 148 149// }}} 150 151namespace __asan { 152 153void InitializePlatformInterceptors() { 154 ASAN_INTERCEPT_FUNC(CreateThread); 155 ASAN_INTERCEPT_FUNC(RaiseException); 156 157// TODO(wwchrome): Win64 uses _C_specific_handler instead. 158#ifndef _WIN64 159 ASAN_INTERCEPT_FUNC(_except_handler3); 160 ASAN_INTERCEPT_FUNC(_except_handler4); 161#endif 162 163 // NtWaitForWorkViaWorkerFactory is always linked dynamically. 164 CHECK(::__interception::OverrideFunction( 165 "NtWaitForWorkViaWorkerFactory", 166 (uptr)WRAP(NtWaitForWorkViaWorkerFactory), 167 (uptr *)&REAL(NtWaitForWorkViaWorkerFactory))); 168} 169 170void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { 171 UNIMPLEMENTED(); 172} 173 174// ---------------------- TSD ---------------- {{{ 175static bool tsd_key_inited = false; 176 177static __declspec(thread) void *fake_tsd = 0; 178 179void AsanTSDInit(void (*destructor)(void *tsd)) { 180 // FIXME: we're ignoring the destructor for now. 181 tsd_key_inited = true; 182} 183 184void *AsanTSDGet() { 185 CHECK(tsd_key_inited); 186 return fake_tsd; 187} 188 189void AsanTSDSet(void *tsd) { 190 CHECK(tsd_key_inited); 191 fake_tsd = tsd; 192} 193 194void PlatformTSDDtor(void *tsd) { 195 AsanThread::TSDDtor(tsd); 196} 197// }}} 198 199// ---------------------- Various stuff ---------------- {{{ 200void *AsanDoesNotSupportStaticLinkage() { 201#if defined(_DEBUG) 202#error Please build the runtime with a non-debug CRT: /MD or /MT 203#endif 204 return 0; 205} 206 207void AsanCheckDynamicRTPrereqs() {} 208 209void AsanCheckIncompatibleRT() {} 210 211void ReadContextStack(void *context, uptr *stack, uptr *ssize) { 212 UNIMPLEMENTED(); 213} 214 215void AsanOnDeadlySignal(int, void *siginfo, void *context) { 216 UNIMPLEMENTED(); 217} 218 219#if SANITIZER_WINDOWS64 220// Exception handler for dealing with shadow memory. 221static LONG CALLBACK 222ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) { 223 static uptr page_size = GetPageSizeCached(); 224 static uptr alloc_granularity = GetMmapGranularity(); 225 // Only handle access violations. 226 if (exception_pointers->ExceptionRecord->ExceptionCode != 227 EXCEPTION_ACCESS_VIOLATION) { 228 return EXCEPTION_CONTINUE_SEARCH; 229 } 230 231 // Only handle access violations that land within the shadow memory. 232 uptr addr = 233 (uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]); 234 235 // Check valid shadow range. 236 if (!AddrIsInShadow(addr)) return EXCEPTION_CONTINUE_SEARCH; 237 238 // This is an access violation while trying to read from the shadow. Commit 239 // the relevant page and let execution continue. 240 241 // Determine the address of the page that is being accessed. 242 uptr page = RoundDownTo(addr, page_size); 243 244 // Query the existing page. 245 MEMORY_BASIC_INFORMATION mem_info = {}; 246 if (::VirtualQuery((LPVOID)page, &mem_info, sizeof(mem_info)) == 0) 247 return EXCEPTION_CONTINUE_SEARCH; 248 249 // Commit the page. 250 uptr result = 251 (uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE); 252 if (result != page) return EXCEPTION_CONTINUE_SEARCH; 253 254 // The page mapping succeeded, so continue execution as usual. 255 return EXCEPTION_CONTINUE_EXECUTION; 256} 257 258#endif 259 260void InitializePlatformExceptionHandlers() { 261#if SANITIZER_WINDOWS64 262 // On Win64, we map memory on demand with access violation handler. 263 // Install our exception handler. 264 CHECK(AddVectoredExceptionHandler(TRUE, &ShadowExceptionHandler)); 265#endif 266} 267 268static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler; 269 270static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) { 271 EXCEPTION_RECORD *exception_record = info->ExceptionRecord; 272 CONTEXT *context = info->ContextRecord; 273 274 if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION || 275 exception_record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) { 276 const char *description = 277 (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) 278 ? "access-violation" 279 : "in-page-error"; 280 SignalContext sig = SignalContext::Create(exception_record, context); 281 ReportDeadlySignal(description, sig); 282 } 283 284 // FIXME: Handle EXCEPTION_STACK_OVERFLOW here. 285 286 return default_seh_handler(info); 287} 288 289// We want to install our own exception handler (EH) to print helpful reports 290// on access violations and whatnot. Unfortunately, the CRT initializers assume 291// they are run before any user code and drop any previously-installed EHs on 292// the floor, so we can't install our handler inside __asan_init. 293// (See crt0dat.c in the CRT sources for the details) 294// 295// Things get even more complicated with the dynamic runtime, as it finishes its 296// initialization before the .exe module CRT begins to initialize. 297// 298// For the static runtime (-MT), it's enough to put a callback to 299// __asan_set_seh_filter in the last section for C initializers. 300// 301// For the dynamic runtime (-MD), we want link the same 302// asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter 303// will be called for each instrumented module. This ensures that at least one 304// __asan_set_seh_filter call happens after the .exe module CRT is initialized. 305extern "C" SANITIZER_INTERFACE_ATTRIBUTE 306int __asan_set_seh_filter() { 307 // We should only store the previous handler if it's not our own handler in 308 // order to avoid loops in the EH chain. 309 auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler); 310 if (prev_seh_handler != &SEHHandler) 311 default_seh_handler = prev_seh_handler; 312 return 0; 313} 314 315#if !ASAN_DYNAMIC 316// The CRT runs initializers in this order: 317// - C initializers, from XIA to XIZ 318// - C++ initializers, from XCA to XCZ 319// Prior to 2015, the CRT set the unhandled exception filter at priority XIY, 320// near the end of C initialization. Starting in 2015, it was moved to the 321// beginning of C++ initialization. We set our priority to XCAB to run 322// immediately after the CRT runs. This way, our exception filter is called 323// first and we can delegate to their filter if appropriate. 324#pragma section(".CRT$XCAB", long, read) // NOLINT 325__declspec(allocate(".CRT$XCAB")) 326 int (*__intercept_seh)() = __asan_set_seh_filter; 327#endif 328// }}} 329} // namespace __asan 330 331#endif // _WIN32 332