| 1 | // RUN: %clang_cl_asan -Od %p/../dll_host.cpp -Fe%t |
| 2 | // RUN: %clang_cl_asan -LD -O2 %s -Fe%t.dll |
| 3 | // RUNX: %run %t %t.dll 2>&1 | FileCheck %s |
| 4 | |
| 5 | // Check that ASan does not CHECK fail when SEH is used around a crash from a |
| 6 | // thread injected by control C. |
| 7 | |
| 8 | #include <stdio.h> |
| 9 | #include <stdlib.h> |
| 10 | #include <windows.h> |
| 11 | |
| 12 | static void __declspec(noinline) CrashOnProcessDetach() { |
| 13 | printf("CrashOnProcessDetach\n" ); |
| 14 | fflush(stdout); |
| 15 | *static_cast<volatile int *>(0) = 0x356; |
| 16 | } |
| 17 | |
| 18 | bool g_is_child = false; |
| 19 | |
| 20 | BOOL WINAPI DllMain(PVOID h, DWORD reason, PVOID reserved) { |
| 21 | if (reason == DLL_PROCESS_DETACH && g_is_child) { |
| 22 | printf("in DllMain DLL_PROCESS_DETACH\n" ); |
| 23 | fflush(stdout); |
| 24 | __try { |
| 25 | CrashOnProcessDetach(); |
| 26 | } __except (1) { |
| 27 | printf("caught crash\n" ); |
| 28 | fflush(stdout); |
| 29 | } |
| 30 | } |
| 31 | return true; |
| 32 | } |
| 33 | |
| 34 | static void run_child() { |
| 35 | // Send this process group Ctrl+C. That should only be this process. |
| 36 | printf(format: "GenerateConsoleCtrlEvent\n" ); |
| 37 | fflush(stdout); |
| 38 | GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); |
| 39 | Sleep(10 * 1000); // Wait 10 seconds, and the process should die. |
| 40 | printf(format: "unexpected execution after interrupt\n" ); |
| 41 | fflush(stdout); |
| 42 | exit(status: 0x42); |
| 43 | } |
| 44 | |
| 45 | static int WINAPI ignore_control_c(DWORD ctrl_type) { |
| 46 | // Don't interrupt the parent. |
| 47 | return ctrl_type == CTRL_C_EVENT; |
| 48 | } |
| 49 | |
| 50 | static int run_parent() { |
| 51 | // Set an environment variable to tell the child process to interrupt itself. |
| 52 | if (!SetEnvironmentVariableW(L"DO_CONTROL_C" , L"1" )) { |
| 53 | printf("SetEnvironmentVariableW failed (0x%8lx).\n" , GetLastError()); |
| 54 | fflush(stdout); |
| 55 | return 2; |
| 56 | } |
| 57 | |
| 58 | // Launch a new process using the current executable with a new console. |
| 59 | // Ctrl-C events are console-wide, so we need a new console. |
| 60 | STARTUPINFOW si; |
| 61 | memset(&si, 0, sizeof(si)); |
| 62 | si.cb = sizeof(si); |
| 63 | // Hides the new console window that we are creating. |
| 64 | si.dwFlags |= STARTF_USESHOWWINDOW; |
| 65 | si.wShowWindow = SW_HIDE; |
| 66 | // Ensures that stdout still goes to the parent despite the new console. |
| 67 | si.dwFlags |= STARTF_USESTDHANDLES; |
| 68 | si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); |
| 69 | si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); |
| 70 | si.hStdError = GetStdHandle(STD_ERROR_HANDLE); |
| 71 | |
| 72 | PROCESS_INFORMATION pi; |
| 73 | memset(&pi, 0, sizeof(pi)); |
| 74 | int flags = CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP | CREATE_NEW_CONSOLE; |
| 75 | if (!CreateProcessW(nullptr, // No module name (use command line) |
| 76 | GetCommandLineW(), // Command line |
| 77 | nullptr, // Process handle not inheritable |
| 78 | nullptr, // Thread handle not inheritable |
| 79 | TRUE, // Set handle inheritance to TRUE |
| 80 | flags, // Flags to give the child a console |
| 81 | nullptr, // Use parent's environment block |
| 82 | nullptr, // Use parent's starting directory |
| 83 | &si, &pi)) { |
| 84 | printf("CreateProcess failed (0x%08lx).\n" , GetLastError()); |
| 85 | fflush(stdout); |
| 86 | return 2; |
| 87 | } |
| 88 | |
| 89 | // Wait until child process exits. |
| 90 | if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) { |
| 91 | printf("WaitForSingleObject failed (0x%08lx).\n" , GetLastError()); |
| 92 | fflush(stdout); |
| 93 | return 2; |
| 94 | } |
| 95 | |
| 96 | // Get the exit code. It should be the one for ctrl-c events. |
| 97 | DWORD rc; |
| 98 | if (!GetExitCodeProcess(pi.hProcess, &rc)) { |
| 99 | printf("GetExitCodeProcess failed (0x%08lx).\n" , GetLastError()); |
| 100 | fflush(stdout); |
| 101 | return 2; |
| 102 | } |
| 103 | if (rc == STATUS_CONTROL_C_EXIT) |
| 104 | printf(format: "child quit with STATUS_CONTROL_C_EXIT\n" ); |
| 105 | else |
| 106 | printf("unexpected exit code: 0x%08lx\n" , rc); |
| 107 | fflush(stdout); |
| 108 | |
| 109 | // Close process and thread handles. |
| 110 | CloseHandle(pi.hProcess); |
| 111 | CloseHandle(pi.hThread); |
| 112 | return 0; |
| 113 | } |
| 114 | |
| 115 | // CHECK: in DllMain DLL_PROCESS_DETACH |
| 116 | // CHECK: CrashOnProcessDetach |
| 117 | // CHECK: caught crash |
| 118 | // CHECK: child quit with STATUS_CONTROL_C_EXIT |
| 119 | |
| 120 | extern "C" int __declspec(dllexport) test_function() { |
| 121 | wchar_t buf[260]; |
| 122 | int len = GetEnvironmentVariableW(L"DO_CONTROL_C" , buf, 260); |
| 123 | if (len > 0) { |
| 124 | g_is_child = true; |
| 125 | run_child(); |
| 126 | } else { |
| 127 | exit(run_parent()); |
| 128 | } |
| 129 | return 0; |
| 130 | } |
| 131 | |