// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t // RUN: %clang_cl_asan -LD -O2 %s -Fe%t.dll // RUNX: %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 <stdio.h> #include <stdlib.h> #include <windows.h> static void __declspec(noinline) CrashOnProcessDetach() { printf("CrashOnProcessDetach\n"); fflush(stdout); *static_cast<volatile int *>(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; }