| // 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; |
| } |