1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
// RUN: %clang_cl_asan -Od %p/../dll_host.cpp -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;
}
|