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("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("unexpected execution after interrupt\n"); 41 fflush(stdout); 42 exit(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("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