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 | |