Index: lib/asan/asan_internal.h =================================================================== --- lib/asan/asan_internal.h +++ lib/asan/asan_internal.h @@ -62,6 +62,9 @@ void AsanInitFromRtl(); +// asan_win.cc +void InitializePlatformExceptionHandlers(); + // asan_rtl.cc void NORETURN ShowStatsAndAbort(); @@ -127,7 +130,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,8 @@ SetCanPoisonMemory(flags()->poison_heap); SetMallocContextSize(common_flags()->malloc_context_size); + InitializePlatformExceptionHandlers(); + 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,56 @@ UNIMPLEMENTED(); } +#if SANITIZER_WINDOWS64 +// Exception handler for dealing with shadow memory. +static LONG CALLBACK +ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) { + static uptr page_size = GetPageSizeCached(); + static uptr alloc_granularity = GetMmapGranularity(); + // 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. + uptr addr = (uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]); + + // Check valid shadow range. + if (!AddrIsInShadow(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. + uptr page = RoundDownTo(addr, page_size); + + // Query the existing page. + MEMORY_BASIC_INFORMATION mem_info = {}; + if (::VirtualQuery((LPVOID)page, &mem_info, sizeof(mem_info)) == 0) + return EXCEPTION_CONTINUE_SEARCH; + + // Commit the page. + uptr result = (uptr)::VirtualAlloc((LPVOID)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; +} + +#endif + +void InitializePlatformExceptionHandlers() { +#if SANITIZER_WINDOWS64 + // On Win64, we map memory on demand with access violation handler. + // Install our exception handler. + if (!AddVectoredExceptionHandler(TRUE, &ShadowExceptionHandler)) { + // It is critical this handler must work. + __debugbreak(); + } +#endif +} + static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler; static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) { Index: lib/sanitizer_common/sanitizer_win.cc =================================================================== --- lib/sanitizer_common/sanitizer_win.cc +++ lib/sanitizer_common/sanitizer_win.cc @@ -171,9 +171,16 @@ void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) { // FIXME: is this really "NoReserve"? On Win32 this does not matter much, // but on Win64 it does. - (void)name; // unsupported - void *p = VirtualAlloc((LPVOID)fixed_addr, size, - MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + (void)name; // unsupported +#if SANITIZER_WINDOWS64 + // On Windows64, use MEM_COMMIT would result in error + // 1455:ERROR_COMMITMENT_LIMIT. + // We use execption handler to commit page on demand. + void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE, PAGE_READWRITE); +#else + void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE); +#endif if (p == 0) Report("ERROR: %s failed to " "allocate %p (%zd) bytes at %p (error code: %d)\n",