Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -545,10 +545,18 @@ void NOINLINE __asan_handle_no_return() { int local_stack; AsanThread *curr_thread = GetCurrentThread(); - CHECK(curr_thread); uptr PageSize = GetPageSizeCached(); - uptr top = curr_thread->stack_top(); - uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1); + uptr top, bottom; + if (curr_thread) { + top = curr_thread->stack_top(); + bottom = ((uptr)&local_stack - PageSize) & ~(PageSize - 1); + } else { + // If we haven't seen this thread, try asking the OS for stack bounds. + uptr tls_addr, tls_size, stack_size; + GetThreadStackAndTls(/*main=*/false, &bottom, &stack_size, &tls_addr, + &tls_size); + top = bottom + stack_size; + } static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M if (top - bottom > kMaxExpectedCleanupSize) { static bool reported_warning = false; @@ -564,7 +572,7 @@ return; } PoisonShadow(bottom, top - bottom, 0); - if (curr_thread->has_fake_stack()) + if (curr_thread && curr_thread->has_fake_stack()) curr_thread->fake_stack()->HandleNoReturn(); } Index: test/asan/TestCases/Windows/dll_control_c.cc =================================================================== --- /dev/null +++ test/asan/TestCases/Windows/dll_control_c.cc @@ -0,0 +1,130 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O2 %s -Fe%t.dll +// RUN: %run %t %t.dll 2>&1 | FileCheck %s + +// Check that ASan does not CHECK fail when SEH is used around a crash from a +// thread injected by control C. + +#include +#include +#include + +static void __declspec(noinline) CrashOnProcessDetach() { + printf("CrashOnProcessDetach\n"); + fflush(stdout); + *static_cast(0) = 0x356; +} + +bool g_is_child = false; + +BOOL WINAPI DllMain(PVOID h, DWORD reason, PVOID reserved) { + if (reason == DLL_PROCESS_DETACH && g_is_child) { + printf("in DllMain DLL_PROCESS_DETACH\n"); + fflush(stdout); + __try { + CrashOnProcessDetach(); + } __except (1) { + printf("caught crash\n"); + fflush(stdout); + } + } + return true; +} + +static void run_child() { + // Send this process group Ctrl+C. That should only be this process. + printf("GenerateConsoleCtrlEvent\n"); + fflush(stdout); + GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); + Sleep(10 * 1000); // Wait 10 seconds, and the process should die. + printf("unexpected execution after interrupt\n"); + fflush(stdout); + exit(0x42); +} + +static int WINAPI ignore_control_c(DWORD ctrl_type) { + // Don't interrupt the parent. + return ctrl_type == CTRL_C_EVENT; +} + +static int run_parent() { + // Set an environment variable to tell the child process to interrupt itself. + if (!SetEnvironmentVariableW(L"DO_CONTROL_C", L"1")) { + printf("SetEnvironmentVariableW failed (0x%8lx).\n", GetLastError()); + fflush(stdout); + return 2; + } + + // Launch a new process using the current executable with a new console. + // Ctrl-C events are console-wide, so we need a new console. + STARTUPINFOW si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + // Hides the new console window that we are creating. + si.dwFlags |= STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + // Ensures that stdout still goes to the parent despite the new console. + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + PROCESS_INFORMATION pi; + memset(&pi, 0, sizeof(pi)); + int flags = CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP | CREATE_NEW_CONSOLE; + if (!CreateProcessW(nullptr, // No module name (use command line) + GetCommandLineW(), // Command line + nullptr, // Process handle not inheritable + nullptr, // Thread handle not inheritable + TRUE, // Set handle inheritance to TRUE + flags, // Flags to give the child a console + nullptr, // Use parent's environment block + nullptr, // Use parent's starting directory + &si, &pi)) { + printf("CreateProcess failed (0x%08lx).\n", GetLastError()); + fflush(stdout); + return 2; + } + + // Wait until child process exits. + if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) { + printf("WaitForSingleObject failed (0x%08lx).\n", GetLastError()); + fflush(stdout); + return 2; + } + + // Get the exit code. It should be the one for ctrl-c events. + DWORD rc; + if (!GetExitCodeProcess(pi.hProcess, &rc)) { + printf("GetExitCodeProcess failed (0x%08lx).\n", GetLastError()); + fflush(stdout); + return 2; + } + if (rc == STATUS_CONTROL_C_EXIT) + printf("child quit with STATUS_CONTROL_C_EXIT\n"); + else + printf("unexpected exit code: 0x%08lx\n", rc); + fflush(stdout); + + // Close process and thread handles. + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 0; +} + +// CHECK: in DllMain DLL_PROCESS_DETACH +// CHECK: CrashOnProcessDetach +// CHECK: caught crash +// CHECK: child quit with STATUS_CONTROL_C_EXIT + +extern "C" int __declspec(dllexport) test_function() { + wchar_t buf[260]; + int len = GetEnvironmentVariableW(L"DO_CONTROL_C", buf, 260); + if (len > 0) { + g_is_child = true; + run_child(); + } else { + exit(run_parent()); + } + return 0; +}