Home | History | Annotate | Line # | Download | only in asan
asan_win.cpp revision 1.1.1.1
      1  1.1  mrg //===-- asan_win.cpp ------------------------------------------------------===//
      2  1.1  mrg //
      3  1.1  mrg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4  1.1  mrg // See https://llvm.org/LICENSE.txt for license information.
      5  1.1  mrg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6  1.1  mrg //
      7  1.1  mrg //===----------------------------------------------------------------------===//
      8  1.1  mrg //
      9  1.1  mrg // This file is a part of AddressSanitizer, an address sanity checker.
     10  1.1  mrg //
     11  1.1  mrg // Windows-specific details.
     12  1.1  mrg //===----------------------------------------------------------------------===//
     13  1.1  mrg 
     14  1.1  mrg #include "sanitizer_common/sanitizer_platform.h"
     15  1.1  mrg #if SANITIZER_WINDOWS
     16  1.1  mrg #define WIN32_LEAN_AND_MEAN
     17  1.1  mrg #include <windows.h>
     18  1.1  mrg 
     19  1.1  mrg #include <stdlib.h>
     20  1.1  mrg 
     21  1.1  mrg #include "asan_interceptors.h"
     22  1.1  mrg #include "asan_internal.h"
     23  1.1  mrg #include "asan_mapping.h"
     24  1.1  mrg #include "asan_report.h"
     25  1.1  mrg #include "asan_stack.h"
     26  1.1  mrg #include "asan_thread.h"
     27  1.1  mrg #include "sanitizer_common/sanitizer_libc.h"
     28  1.1  mrg #include "sanitizer_common/sanitizer_mutex.h"
     29  1.1  mrg #include "sanitizer_common/sanitizer_win.h"
     30  1.1  mrg #include "sanitizer_common/sanitizer_win_defs.h"
     31  1.1  mrg 
     32  1.1  mrg using namespace __asan;
     33  1.1  mrg 
     34  1.1  mrg extern "C" {
     35  1.1  mrg SANITIZER_INTERFACE_ATTRIBUTE
     36  1.1  mrg int __asan_should_detect_stack_use_after_return() {
     37  1.1  mrg   __asan_init();
     38  1.1  mrg   return __asan_option_detect_stack_use_after_return;
     39  1.1  mrg }
     40  1.1  mrg 
     41  1.1  mrg SANITIZER_INTERFACE_ATTRIBUTE
     42  1.1  mrg uptr __asan_get_shadow_memory_dynamic_address() {
     43  1.1  mrg   __asan_init();
     44  1.1  mrg   return __asan_shadow_memory_dynamic_address;
     45  1.1  mrg }
     46  1.1  mrg }  // extern "C"
     47  1.1  mrg 
     48  1.1  mrg // ---------------------- Windows-specific interceptors ---------------- {{{
     49  1.1  mrg static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;
     50  1.1  mrg static LPTOP_LEVEL_EXCEPTION_FILTER user_seh_handler;
     51  1.1  mrg 
     52  1.1  mrg extern "C" SANITIZER_INTERFACE_ATTRIBUTE
     53  1.1  mrg long __asan_unhandled_exception_filter(EXCEPTION_POINTERS *info) {
     54  1.1  mrg   EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
     55  1.1  mrg   CONTEXT *context = info->ContextRecord;
     56  1.1  mrg 
     57  1.1  mrg   // FIXME: Handle EXCEPTION_STACK_OVERFLOW here.
     58  1.1  mrg 
     59  1.1  mrg   SignalContext sig(exception_record, context);
     60  1.1  mrg   ReportDeadlySignal(sig);
     61  1.1  mrg   UNREACHABLE("returned from reporting deadly signal");
     62  1.1  mrg }
     63  1.1  mrg 
     64  1.1  mrg // Wrapper SEH Handler. If the exception should be handled by asan, we call
     65  1.1  mrg // __asan_unhandled_exception_filter, otherwise, we execute the user provided
     66  1.1  mrg // exception handler or the default.
     67  1.1  mrg static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
     68  1.1  mrg   DWORD exception_code = info->ExceptionRecord->ExceptionCode;
     69  1.1  mrg   if (__sanitizer::IsHandledDeadlyException(exception_code))
     70  1.1  mrg     return __asan_unhandled_exception_filter(info);
     71  1.1  mrg   if (user_seh_handler)
     72  1.1  mrg     return user_seh_handler(info);
     73  1.1  mrg   // Bubble out to the default exception filter.
     74  1.1  mrg   if (default_seh_handler)
     75  1.1  mrg     return default_seh_handler(info);
     76  1.1  mrg   return EXCEPTION_CONTINUE_SEARCH;
     77  1.1  mrg }
     78  1.1  mrg 
     79  1.1  mrg INTERCEPTOR_WINAPI(LPTOP_LEVEL_EXCEPTION_FILTER, SetUnhandledExceptionFilter,
     80  1.1  mrg                    LPTOP_LEVEL_EXCEPTION_FILTER ExceptionFilter) {
     81  1.1  mrg   CHECK(REAL(SetUnhandledExceptionFilter));
     82  1.1  mrg   if (ExceptionFilter == &SEHHandler)
     83  1.1  mrg     return REAL(SetUnhandledExceptionFilter)(ExceptionFilter);
     84  1.1  mrg   // We record the user provided exception handler to be called for all the
     85  1.1  mrg   // exceptions unhandled by asan.
     86  1.1  mrg   Swap(ExceptionFilter, user_seh_handler);
     87  1.1  mrg   return ExceptionFilter;
     88  1.1  mrg }
     89  1.1  mrg 
     90  1.1  mrg INTERCEPTOR_WINAPI(void, RtlRaiseException, EXCEPTION_RECORD *ExceptionRecord) {
     91  1.1  mrg   CHECK(REAL(RtlRaiseException));
     92  1.1  mrg   // This is a noreturn function, unless it's one of the exceptions raised to
     93  1.1  mrg   // communicate with the debugger, such as the one from OutputDebugString.
     94  1.1  mrg   if (ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C)
     95  1.1  mrg     __asan_handle_no_return();
     96  1.1  mrg   REAL(RtlRaiseException)(ExceptionRecord);
     97  1.1  mrg }
     98  1.1  mrg 
     99  1.1  mrg INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
    100  1.1  mrg   CHECK(REAL(RaiseException));
    101  1.1  mrg   __asan_handle_no_return();
    102  1.1  mrg   REAL(RaiseException)(a, b, c, d);
    103  1.1  mrg }
    104  1.1  mrg 
    105  1.1  mrg #ifdef _WIN64
    106  1.1  mrg 
    107  1.1  mrg INTERCEPTOR_WINAPI(EXCEPTION_DISPOSITION, __C_specific_handler,
    108  1.1  mrg                    _EXCEPTION_RECORD *a, void *b, _CONTEXT *c,
    109  1.1  mrg                    _DISPATCHER_CONTEXT *d) {
    110  1.1  mrg   CHECK(REAL(__C_specific_handler));
    111  1.1  mrg   __asan_handle_no_return();
    112  1.1  mrg   return REAL(__C_specific_handler)(a, b, c, d);
    113  1.1  mrg }
    114  1.1  mrg 
    115  1.1  mrg #else
    116  1.1  mrg 
    117  1.1  mrg INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
    118  1.1  mrg   CHECK(REAL(_except_handler3));
    119  1.1  mrg   __asan_handle_no_return();
    120  1.1  mrg   return REAL(_except_handler3)(a, b, c, d);
    121  1.1  mrg }
    122  1.1  mrg 
    123  1.1  mrg #if ASAN_DYNAMIC
    124  1.1  mrg // This handler is named differently in -MT and -MD CRTs.
    125  1.1  mrg #define _except_handler4 _except_handler4_common
    126  1.1  mrg #endif
    127  1.1  mrg INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
    128  1.1  mrg   CHECK(REAL(_except_handler4));
    129  1.1  mrg   __asan_handle_no_return();
    130  1.1  mrg   return REAL(_except_handler4)(a, b, c, d);
    131  1.1  mrg }
    132  1.1  mrg #endif
    133  1.1  mrg 
    134  1.1  mrg static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
    135  1.1  mrg   AsanThread *t = (AsanThread *)arg;
    136  1.1  mrg   SetCurrentThread(t);
    137  1.1  mrg   return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr);
    138  1.1  mrg }
    139  1.1  mrg 
    140  1.1  mrg INTERCEPTOR_WINAPI(HANDLE, CreateThread, LPSECURITY_ATTRIBUTES security,
    141  1.1  mrg                    SIZE_T stack_size, LPTHREAD_START_ROUTINE start_routine,
    142  1.1  mrg                    void *arg, DWORD thr_flags, DWORD *tid) {
    143  1.1  mrg   // Strict init-order checking is thread-hostile.
    144  1.1  mrg   if (flags()->strict_init_order)
    145  1.1  mrg     StopInitOrderChecking();
    146  1.1  mrg   GET_STACK_TRACE_THREAD;
    147  1.1  mrg   // FIXME: The CreateThread interceptor is not the same as a pthread_create
    148  1.1  mrg   // one.  This is a bandaid fix for PR22025.
    149  1.1  mrg   bool detached = false;  // FIXME: how can we determine it on Windows?
    150  1.1  mrg   u32 current_tid = GetCurrentTidOrInvalid();
    151  1.1  mrg   AsanThread *t =
    152  1.1  mrg       AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
    153  1.1  mrg   return REAL(CreateThread)(security, stack_size, asan_thread_start, t,
    154  1.1  mrg                             thr_flags, tid);
    155  1.1  mrg }
    156  1.1  mrg 
    157  1.1  mrg // }}}
    158  1.1  mrg 
    159  1.1  mrg namespace __asan {
    160  1.1  mrg 
    161  1.1  mrg void InitializePlatformInterceptors() {
    162  1.1  mrg   // The interceptors were not designed to be removable, so we have to keep this
    163  1.1  mrg   // module alive for the life of the process.
    164  1.1  mrg   HMODULE pinned;
    165  1.1  mrg   CHECK(GetModuleHandleExW(
    166  1.1  mrg       GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
    167  1.1  mrg       (LPCWSTR)&InitializePlatformInterceptors, &pinned));
    168  1.1  mrg 
    169  1.1  mrg   ASAN_INTERCEPT_FUNC(CreateThread);
    170  1.1  mrg   ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter);
    171  1.1  mrg 
    172  1.1  mrg #ifdef _WIN64
    173  1.1  mrg   ASAN_INTERCEPT_FUNC(__C_specific_handler);
    174  1.1  mrg #else
    175  1.1  mrg   ASAN_INTERCEPT_FUNC(_except_handler3);
    176  1.1  mrg   ASAN_INTERCEPT_FUNC(_except_handler4);
    177  1.1  mrg #endif
    178  1.1  mrg 
    179  1.1  mrg   // Try to intercept kernel32!RaiseException, and if that fails, intercept
    180  1.1  mrg   // ntdll!RtlRaiseException instead.
    181  1.1  mrg   if (!::__interception::OverrideFunction("RaiseException",
    182  1.1  mrg                                           (uptr)WRAP(RaiseException),
    183  1.1  mrg                                           (uptr *)&REAL(RaiseException))) {
    184  1.1  mrg     CHECK(::__interception::OverrideFunction("RtlRaiseException",
    185  1.1  mrg                                              (uptr)WRAP(RtlRaiseException),
    186  1.1  mrg                                              (uptr *)&REAL(RtlRaiseException)));
    187  1.1  mrg   }
    188  1.1  mrg }
    189  1.1  mrg 
    190  1.1  mrg void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
    191  1.1  mrg   UNIMPLEMENTED();
    192  1.1  mrg }
    193  1.1  mrg 
    194  1.1  mrg // ---------------------- TSD ---------------- {{{
    195  1.1  mrg static bool tsd_key_inited = false;
    196  1.1  mrg 
    197  1.1  mrg static __declspec(thread) void *fake_tsd = 0;
    198  1.1  mrg 
    199  1.1  mrg // https://docs.microsoft.com/en-us/windows/desktop/api/winternl/ns-winternl-_teb
    200  1.1  mrg // "[This structure may be altered in future versions of Windows. Applications
    201  1.1  mrg // should use the alternate functions listed in this topic.]"
    202  1.1  mrg typedef struct _TEB {
    203  1.1  mrg   PVOID Reserved1[12];
    204  1.1  mrg   // PVOID ThreadLocalStoragePointer; is here, at the last field in Reserved1.
    205  1.1  mrg   PVOID ProcessEnvironmentBlock;
    206  1.1  mrg   PVOID Reserved2[399];
    207  1.1  mrg   BYTE Reserved3[1952];
    208  1.1  mrg   PVOID TlsSlots[64];
    209  1.1  mrg   BYTE Reserved4[8];
    210  1.1  mrg   PVOID Reserved5[26];
    211  1.1  mrg   PVOID ReservedForOle;
    212  1.1  mrg   PVOID Reserved6[4];
    213  1.1  mrg   PVOID TlsExpansionSlots;
    214  1.1  mrg } TEB, *PTEB;
    215  1.1  mrg 
    216  1.1  mrg constexpr size_t TEB_RESERVED_FIELDS_THREAD_LOCAL_STORAGE_OFFSET = 11;
    217  1.1  mrg BOOL IsTlsInitialized() {
    218  1.1  mrg   PTEB teb = (PTEB)NtCurrentTeb();
    219  1.1  mrg   return teb->Reserved1[TEB_RESERVED_FIELDS_THREAD_LOCAL_STORAGE_OFFSET] !=
    220  1.1  mrg          nullptr;
    221  1.1  mrg }
    222  1.1  mrg 
    223  1.1  mrg void AsanTSDInit(void (*destructor)(void *tsd)) {
    224  1.1  mrg   // FIXME: we're ignoring the destructor for now.
    225  1.1  mrg   tsd_key_inited = true;
    226  1.1  mrg }
    227  1.1  mrg 
    228  1.1  mrg void *AsanTSDGet() {
    229  1.1  mrg   CHECK(tsd_key_inited);
    230  1.1  mrg   return IsTlsInitialized() ? fake_tsd : nullptr;
    231  1.1  mrg }
    232  1.1  mrg 
    233  1.1  mrg void AsanTSDSet(void *tsd) {
    234  1.1  mrg   CHECK(tsd_key_inited);
    235  1.1  mrg   fake_tsd = tsd;
    236  1.1  mrg }
    237  1.1  mrg 
    238  1.1  mrg void PlatformTSDDtor(void *tsd) { AsanThread::TSDDtor(tsd); }
    239  1.1  mrg // }}}
    240  1.1  mrg 
    241  1.1  mrg // ---------------------- Various stuff ---------------- {{{
    242  1.1  mrg void *AsanDoesNotSupportStaticLinkage() {
    243  1.1  mrg #if defined(_DEBUG)
    244  1.1  mrg #error Please build the runtime with a non-debug CRT: /MD or /MT
    245  1.1  mrg #endif
    246  1.1  mrg   return 0;
    247  1.1  mrg }
    248  1.1  mrg 
    249  1.1  mrg uptr FindDynamicShadowStart() {
    250  1.1  mrg   uptr granularity = GetMmapGranularity();
    251  1.1  mrg   uptr alignment = 8 * granularity;
    252  1.1  mrg   uptr left_padding = granularity;
    253  1.1  mrg   uptr space_size = kHighShadowEnd + left_padding;
    254  1.1  mrg   uptr shadow_start = FindAvailableMemoryRange(space_size, alignment,
    255  1.1  mrg                                                granularity, nullptr, nullptr);
    256  1.1  mrg   CHECK_NE((uptr)0, shadow_start);
    257  1.1  mrg   CHECK(IsAligned(shadow_start, alignment));
    258  1.1  mrg   return shadow_start;
    259  1.1  mrg }
    260  1.1  mrg 
    261  1.1  mrg void AsanCheckDynamicRTPrereqs() {}
    262  1.1  mrg 
    263  1.1  mrg void AsanCheckIncompatibleRT() {}
    264  1.1  mrg 
    265  1.1  mrg void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
    266  1.1  mrg   UNIMPLEMENTED();
    267  1.1  mrg }
    268  1.1  mrg 
    269  1.1  mrg void AsanOnDeadlySignal(int, void *siginfo, void *context) { UNIMPLEMENTED(); }
    270  1.1  mrg 
    271  1.1  mrg #if SANITIZER_WINDOWS64
    272  1.1  mrg // Exception handler for dealing with shadow memory.
    273  1.1  mrg static LONG CALLBACK
    274  1.1  mrg ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
    275  1.1  mrg   uptr page_size = GetPageSizeCached();
    276  1.1  mrg   // Only handle access violations.
    277  1.1  mrg   if (exception_pointers->ExceptionRecord->ExceptionCode !=
    278  1.1  mrg           EXCEPTION_ACCESS_VIOLATION ||
    279  1.1  mrg       exception_pointers->ExceptionRecord->NumberParameters < 2) {
    280  1.1  mrg     __asan_handle_no_return();
    281  1.1  mrg     return EXCEPTION_CONTINUE_SEARCH;
    282  1.1  mrg   }
    283  1.1  mrg 
    284  1.1  mrg   // Only handle access violations that land within the shadow memory.
    285  1.1  mrg   uptr addr =
    286  1.1  mrg       (uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]);
    287  1.1  mrg 
    288  1.1  mrg   // Check valid shadow range.
    289  1.1  mrg   if (!AddrIsInShadow(addr)) {
    290  1.1  mrg     __asan_handle_no_return();
    291  1.1  mrg     return EXCEPTION_CONTINUE_SEARCH;
    292  1.1  mrg   }
    293  1.1  mrg 
    294  1.1  mrg   // This is an access violation while trying to read from the shadow. Commit
    295  1.1  mrg   // the relevant page and let execution continue.
    296  1.1  mrg 
    297  1.1  mrg   // Determine the address of the page that is being accessed.
    298  1.1  mrg   uptr page = RoundDownTo(addr, page_size);
    299  1.1  mrg 
    300  1.1  mrg   // Commit the page.
    301  1.1  mrg   uptr result =
    302  1.1  mrg       (uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE);
    303  1.1  mrg   if (result != page)
    304  1.1  mrg     return EXCEPTION_CONTINUE_SEARCH;
    305  1.1  mrg 
    306  1.1  mrg   // The page mapping succeeded, so continue execution as usual.
    307  1.1  mrg   return EXCEPTION_CONTINUE_EXECUTION;
    308  1.1  mrg }
    309  1.1  mrg 
    310  1.1  mrg #endif
    311  1.1  mrg 
    312  1.1  mrg void InitializePlatformExceptionHandlers() {
    313  1.1  mrg #if SANITIZER_WINDOWS64
    314  1.1  mrg   // On Win64, we map memory on demand with access violation handler.
    315  1.1  mrg   // Install our exception handler.
    316  1.1  mrg   CHECK(AddVectoredExceptionHandler(TRUE, &ShadowExceptionHandler));
    317  1.1  mrg #endif
    318  1.1  mrg }
    319  1.1  mrg 
    320  1.1  mrg bool IsSystemHeapAddress(uptr addr) {
    321  1.1  mrg   return ::HeapValidate(GetProcessHeap(), 0, (void *)addr) != FALSE;
    322  1.1  mrg }
    323  1.1  mrg 
    324  1.1  mrg // We want to install our own exception handler (EH) to print helpful reports
    325  1.1  mrg // on access violations and whatnot.  Unfortunately, the CRT initializers assume
    326  1.1  mrg // they are run before any user code and drop any previously-installed EHs on
    327  1.1  mrg // the floor, so we can't install our handler inside __asan_init.
    328  1.1  mrg // (See crt0dat.c in the CRT sources for the details)
    329  1.1  mrg //
    330  1.1  mrg // Things get even more complicated with the dynamic runtime, as it finishes its
    331  1.1  mrg // initialization before the .exe module CRT begins to initialize.
    332  1.1  mrg //
    333  1.1  mrg // For the static runtime (-MT), it's enough to put a callback to
    334  1.1  mrg // __asan_set_seh_filter in the last section for C initializers.
    335  1.1  mrg //
    336  1.1  mrg // For the dynamic runtime (-MD), we want link the same
    337  1.1  mrg // asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter
    338  1.1  mrg // will be called for each instrumented module.  This ensures that at least one
    339  1.1  mrg // __asan_set_seh_filter call happens after the .exe module CRT is initialized.
    340  1.1  mrg extern "C" SANITIZER_INTERFACE_ATTRIBUTE int __asan_set_seh_filter() {
    341  1.1  mrg   // We should only store the previous handler if it's not our own handler in
    342  1.1  mrg   // order to avoid loops in the EH chain.
    343  1.1  mrg   auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler);
    344  1.1  mrg   if (prev_seh_handler != &SEHHandler)
    345  1.1  mrg     default_seh_handler = prev_seh_handler;
    346  1.1  mrg   return 0;
    347  1.1  mrg }
    348  1.1  mrg 
    349  1.1  mrg bool HandleDlopenInit() {
    350  1.1  mrg   // Not supported on this platform.
    351  1.1  mrg   static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
    352  1.1  mrg                 "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
    353  1.1  mrg   return false;
    354  1.1  mrg }
    355  1.1  mrg 
    356  1.1  mrg #if !ASAN_DYNAMIC
    357  1.1  mrg // The CRT runs initializers in this order:
    358  1.1  mrg // - C initializers, from XIA to XIZ
    359  1.1  mrg // - C++ initializers, from XCA to XCZ
    360  1.1  mrg // Prior to 2015, the CRT set the unhandled exception filter at priority XIY,
    361  1.1  mrg // near the end of C initialization. Starting in 2015, it was moved to the
    362  1.1  mrg // beginning of C++ initialization. We set our priority to XCAB to run
    363  1.1  mrg // immediately after the CRT runs. This way, our exception filter is called
    364  1.1  mrg // first and we can delegate to their filter if appropriate.
    365  1.1  mrg #pragma section(".CRT$XCAB", long, read)
    366  1.1  mrg __declspec(allocate(".CRT$XCAB")) int (*__intercept_seh)() =
    367  1.1  mrg     __asan_set_seh_filter;
    368  1.1  mrg 
    369  1.1  mrg // Piggyback on the TLS initialization callback directory to initialize asan as
    370  1.1  mrg // early as possible. Initializers in .CRT$XL* are called directly by ntdll,
    371  1.1  mrg // which run before the CRT. Users also add code to .CRT$XLC, so it's important
    372  1.1  mrg // to run our initializers first.
    373  1.1  mrg static void NTAPI asan_thread_init(void *module, DWORD reason, void *reserved) {
    374  1.1  mrg   if (reason == DLL_PROCESS_ATTACH)
    375  1.1  mrg     __asan_init();
    376  1.1  mrg }
    377  1.1  mrg 
    378  1.1  mrg #pragma section(".CRT$XLAB", long, read)
    379  1.1  mrg __declspec(allocate(".CRT$XLAB")) void(NTAPI *__asan_tls_init)(
    380  1.1  mrg     void *, unsigned long, void *) = asan_thread_init;
    381  1.1  mrg #endif
    382  1.1  mrg 
    383  1.1  mrg static void NTAPI asan_thread_exit(void *module, DWORD reason, void *reserved) {
    384  1.1  mrg   if (reason == DLL_THREAD_DETACH) {
    385  1.1  mrg     // Unpoison the thread's stack because the memory may be re-used.
    386  1.1  mrg     NT_TIB *tib = (NT_TIB *)NtCurrentTeb();
    387  1.1  mrg     uptr stackSize = (uptr)tib->StackBase - (uptr)tib->StackLimit;
    388  1.1  mrg     __asan_unpoison_memory_region(tib->StackLimit, stackSize);
    389  1.1  mrg   }
    390  1.1  mrg }
    391  1.1  mrg 
    392  1.1  mrg #pragma section(".CRT$XLY", long, read)
    393  1.1  mrg __declspec(allocate(".CRT$XLY")) void(NTAPI *__asan_tls_exit)(
    394  1.1  mrg     void *, unsigned long, void *) = asan_thread_exit;
    395  1.1  mrg 
    396  1.1  mrg WIN_FORCE_LINK(__asan_dso_reg_hook)
    397  1.1  mrg 
    398  1.1  mrg // }}}
    399  1.1  mrg }  // namespace __asan
    400  1.1  mrg 
    401  1.1  mrg #endif  // SANITIZER_WINDOWS
    402