reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    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;
}