Index: lib/asan/asan_internal.h =================================================================== --- lib/asan/asan_internal.h +++ lib/asan/asan_internal.h @@ -62,6 +62,11 @@ void AsanInitFromRtl(); +#if SANITIZER_WINDOWS64 +// asan_win.cc +void InitializeExceptionHandlerOnWindows64(); +#endif + // asan_rtl.cc void NORETURN ShowStatsAndAbort(); @@ -127,7 +132,6 @@ // Used to avoid infinite recursion in __asan_init(). extern bool asan_init_is_running; extern void (*death_callback)(void); - // These magic values are written to shadow for better error reporting. const int kAsanHeapLeftRedzoneMagic = 0xfa; const int kAsanHeapRightRedzoneMagic = 0xfb; Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -420,6 +420,11 @@ SetCanPoisonMemory(flags()->poison_heap); SetMallocContextSize(common_flags()->malloc_context_size); +#if SANITIZER_WINDOWS64 + // On Win64, we map memory on demand with access violation handler. + InitializeExceptionHandlerOnWindows64(); +#endif + InitializeHighMemEnd(); // Make sure we are not statically linked. Index: lib/asan/asan_win.cc =================================================================== --- lib/asan/asan_win.cc +++ lib/asan/asan_win.cc @@ -24,6 +24,7 @@ #include "asan_report.h" #include "asan_stack.h" #include "asan_thread.h" +#include "asan_mapping.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mutex.h" @@ -215,6 +216,76 @@ UNIMPLEMENTED(); } +static SIZE_T page_size = 0; +static SIZE_T alloc_granularity = 0; + +#if SANITIZER_WINDOWS64 +// Exception handler for dealing with shadow memory. +static LONG CALLBACK +ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) { + // One-time init. + if (page_size == 0) { + SYSTEM_INFO system_info = {}; + ::GetSystemInfo(&system_info); + page_size = system_info.dwPageSize; + alloc_granularity = system_info.dwAllocationGranularity; + } + // Only handle access violations. + if (exception_pointers->ExceptionRecord->ExceptionCode != + EXCEPTION_ACCESS_VIOLATION) { + return EXCEPTION_CONTINUE_SEARCH; + } + + // Only handle access violations that land within the shadow memory. + LPVOID addr = reinterpret_cast( + exception_pointers->ExceptionRecord->ExceptionInformation[1]); + + // Check valid shadow range. + if (!AddrIsInShadow((uptr)addr)) { + return EXCEPTION_CONTINUE_SEARCH; + } + + // This is an access violation while trying to read from the shadow. Commit + // the relevant page and let execution continue. + + // Determine the address of the page that is being accessed. + LPVOID page = reinterpret_cast(reinterpret_cast(addr) & + ~(page_size - 1)); + + // Query the existing page. + MEMORY_BASIC_INFORMATION mem_info = {}; + if (::VirtualQuery(page, &mem_info, sizeof(mem_info)) == 0) + return EXCEPTION_CONTINUE_SEARCH; + + // If the memory isn't part of any reservation, then reserve the chunk of + // pages. + if (mem_info.AllocationBase == 0) { + LPVOID chunk = reinterpret_cast(reinterpret_cast(addr) & + ~(alloc_granularity - 1)); + auto result = + ::VirtualAlloc(chunk, alloc_granularity, MEM_RESERVE, PAGE_READONLY); + if (result != chunk) return EXCEPTION_CONTINUE_SEARCH; + } + + // Commit the page. + auto result = ::VirtualAlloc(page, page_size, MEM_COMMIT, PAGE_READWRITE); + if (result != page) return EXCEPTION_CONTINUE_SEARCH; + + // The page mapping succeeded, so continue execution as usual. + return EXCEPTION_CONTINUE_EXECUTION; +} + +void InitializeExceptionHandlerOnWindows64() { + // Install our exception handler. + auto handler = AddVectoredExceptionHandler(TRUE, &ShadowExceptionHandler); + if (handler == NULL) { + // It is critical this handler must work. + __debugbreak(); + } +} +#endif + + static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler; static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {