[compiler-rt] r242948 - [asan] Make __asan_handle_no_return tolerate unregistered threads

Reid Kleckner reid at kleckner.net
Wed Jul 22 14:58:31 PDT 2015


Author: rnk
Date: Wed Jul 22 16:58:31 2015
New Revision: 242948

URL: http://llvm.org/viewvc/llvm-project?rev=242948&view=rev
Log:
[asan] Make __asan_handle_no_return tolerate unregistered threads

Summary:
On Windows, thread injection by the kernel or other running processes is
a fairly common occurrence, so ASan should be resilient to it.  The
comments on GetCurrentThread() say that it can return null, so we
shouldn't be CHECK failing if it does.

Sending control-C is one way to get the kernel to inject a thread into
your process, so I wrote a test around it.

Reviewers: llvm-commits

Subscribers: samsonov

Differential Revision: http://reviews.llvm.org/D11426

Added:
    compiler-rt/trunk/test/asan/TestCases/Windows/dll_control_c.cc
Modified:
    compiler-rt/trunk/lib/asan/asan_rtl.cc

Modified: compiler-rt/trunk/lib/asan/asan_rtl.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_rtl.cc?rev=242948&r1=242947&r2=242948&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_rtl.cc (original)
+++ compiler-rt/trunk/lib/asan/asan_rtl.cc Wed Jul 22 16:58:31 2015
@@ -545,10 +545,18 @@ int NOINLINE __asan_set_error_exit_code(
 void NOINLINE __asan_handle_no_return() {
   int local_stack;
   AsanThread *curr_thread = GetCurrentThread();
-  CHECK(curr_thread);
   uptr PageSize = GetPageSizeCached();
-  uptr top = curr_thread->stack_top();
-  uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1);
+  uptr top, bottom;
+  if (curr_thread) {
+    top = curr_thread->stack_top();
+    bottom = ((uptr)&local_stack - PageSize) & ~(PageSize - 1);
+  } else {
+    // If we haven't seen this thread, try asking the OS for stack bounds.
+    uptr tls_addr, tls_size, stack_size;
+    GetThreadStackAndTls(/*main=*/false, &bottom, &stack_size, &tls_addr,
+                         &tls_size);
+    top = bottom + stack_size;
+  }
   static const uptr kMaxExpectedCleanupSize = 64 << 20;  // 64M
   if (top - bottom > kMaxExpectedCleanupSize) {
     static bool reported_warning = false;
@@ -564,7 +572,7 @@ void NOINLINE __asan_handle_no_return()
     return;
   }
   PoisonShadow(bottom, top - bottom, 0);
-  if (curr_thread->has_fake_stack())
+  if (curr_thread && curr_thread->has_fake_stack())
     curr_thread->fake_stack()->HandleNoReturn();
 }
 

Added: compiler-rt/trunk/test/asan/TestCases/Windows/dll_control_c.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Windows/dll_control_c.cc?rev=242948&view=auto
==============================================================================
--- compiler-rt/trunk/test/asan/TestCases/Windows/dll_control_c.cc (added)
+++ compiler-rt/trunk/test/asan/TestCases/Windows/dll_control_c.cc Wed Jul 22 16:58:31 2015
@@ -0,0 +1,130 @@
+// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t
+// RUN: %clang_cl_asan -LD -O2 %s -Fe%t.dll
+// RUN: %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;
+}





More information about the llvm-commits mailing list