[compiler-rt] r232499 - [ASan/Win] Fix a CHECK failure when an exception is thrown from a callback passed to BindIoCompletionCallback
Timur Iskhodzhanov
timurrrr at google.com
Tue Mar 17 09:51:00 PDT 2015
Author: timurrrr
Date: Tue Mar 17 11:50:59 2015
New Revision: 232499
URL: http://llvm.org/viewvc/llvm-project?rev=232499&view=rev
Log:
[ASan/Win] Fix a CHECK failure when an exception is thrown from a callback passed to BindIoCompletionCallback
This also simplifies how we handle QueueUserWorkItem
Added:
compiler-rt/trunk/test/asan/TestCases/Windows/bind_io_completion_callback.cc
compiler-rt/trunk/test/asan/TestCases/Windows/queue_user_work_item_report.cc
Modified:
compiler-rt/trunk/lib/asan/asan_win.cc
compiler-rt/trunk/lib/interception/interception_win.cc
Modified: compiler-rt/trunk/lib/asan/asan_win.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_win.cc?rev=232499&r1=232498&r2=232499&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_win.cc (original)
+++ compiler-rt/trunk/lib/asan/asan_win.cc Tue Mar 17 11:50:59 2015
@@ -102,61 +102,51 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread,
}
namespace {
-struct UserWorkItemInfo {
- DWORD (__stdcall *function)(void *arg);
- void *arg;
- u32 parent_tid;
-};
-
BlockingMutex mu_for_thread_tracking(LINKER_INITIALIZED);
-// QueueUserWorkItem may silently create a thread we should keep track of.
-// We achieve this by wrapping the user-supplied work items with our function.
-DWORD __stdcall QueueUserWorkItemWrapper(void *arg) {
- UserWorkItemInfo *item = (UserWorkItemInfo *)arg;
-
- {
- // FIXME: GetCurrentThread relies on TSD, which might not play well with
- // system thread pools. We might want to use something like reference
- // counting to zero out GetCurrentThread() underlying storage when the last
- // work item finishes? Or can we disable reclaiming of threads in the pool?
- BlockingMutexLock l(&mu_for_thread_tracking);
- AsanThread *t = __asan::GetCurrentThread();
- if (!t) {
- GET_STACK_TRACE_THREAD;
- t = AsanThread::Create(/* start_routine */ nullptr, /* arg */ nullptr,
- item->parent_tid, &stack, /* detached */ true);
- t->Init();
- asanThreadRegistry().StartThread(t->tid(), 0, 0);
- SetCurrentThread(t);
- }
- }
-
- DWORD ret = item->function(item->arg);
- delete item;
- return ret;
+void EnsureWorkerThreadRegistered() {
+ // FIXME: GetCurrentThread relies on TSD, which might not play well with
+ // system thread pools. We might want to use something like reference
+ // counting to zero out GetCurrentThread() underlying storage when the last
+ // work item finishes? Or can we disable reclaiming of threads in the pool?
+ BlockingMutexLock l(&mu_for_thread_tracking);
+ if (__asan::GetCurrentThread())
+ return;
+
+ AsanThread *t = AsanThread::Create(
+ /* start_routine */ nullptr, /* arg */ nullptr,
+ /* parent_tid */ -1, /* stack */ nullptr, /* detached */ true);
+ t->Init();
+ asanThreadRegistry().StartThread(t->tid(), 0, 0);
+ SetCurrentThread(t);
}
} // namespace
-INTERCEPTOR_WINAPI(BOOL, QueueUserWorkItem, LPTHREAD_START_ROUTINE function,
- PVOID arg, ULONG flags) {
- UserWorkItemInfo *work_item_info = new UserWorkItemInfo;
- work_item_info->function = function;
- work_item_info->arg = arg;
- work_item_info->parent_tid = GetCurrentTidOrInvalid();
- return REAL(QueueUserWorkItem)(QueueUserWorkItemWrapper,
- work_item_info, flags);
+INTERCEPTOR_WINAPI(DWORD, NtWaitForWorkViaWorkerFactory, DWORD a, DWORD b) {
+ // NtWaitForWorkViaWorkerFactory is called from system worker pool threads to
+ // query work scheduled by BindIoCompletionCallback, QueueUserWorkItem, etc.
+ // System worker pool threads are created at arbitraty point in time and
+ // without using CreateThread, so we wrap NtWaitForWorkViaWorkerFactory
+ // instead and don't register a specific parent_tid/stack.
+ EnsureWorkerThreadRegistered();
+ return REAL(NtWaitForWorkViaWorkerFactory)(a, b);
}
+
// }}}
namespace __asan {
void InitializePlatformInterceptors() {
ASAN_INTERCEPT_FUNC(CreateThread);
- ASAN_INTERCEPT_FUNC(QueueUserWorkItem);
ASAN_INTERCEPT_FUNC(RaiseException);
ASAN_INTERCEPT_FUNC(_except_handler3);
ASAN_INTERCEPT_FUNC(_except_handler4);
+
+ // NtWaitForWorkViaWorkerFactory is always linked dynamically.
+ CHECK(::__interception::OverrideFunction(
+ "NtWaitForWorkViaWorkerFactory",
+ (uptr)WRAP(NtWaitForWorkViaWorkerFactory),
+ (uptr *)&REAL(NtWaitForWorkViaWorkerFactory)));
}
// ---------------------- TSD ---------------- {{{
Modified: compiler-rt/trunk/lib/interception/interception_win.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/interception/interception_win.cc?rev=232499&r1=232498&r2=232499&view=diff
==============================================================================
--- compiler-rt/trunk/lib/interception/interception_win.cc (original)
+++ compiler-rt/trunk/lib/interception/interception_win.cc Tue Mar 17 11:50:59 2015
@@ -84,6 +84,7 @@ static size_t RoundUpToInstrBoundary(siz
cursor += 2;
continue;
case '\xE9': // E9 XX YY ZZ WW = jmp WWZZYYXX
+ case '\xB8': // B8 XX YY ZZ WW = mov eax, WWZZYYXX
cursor += 5;
continue;
}
@@ -182,10 +183,14 @@ bool OverrideFunction(uptr old_func, upt
}
static const void **InterestingDLLsAvailable() {
- const char *InterestingDLLs[] = {"kernel32.dll",
- "msvcr110.dll", // VS2012
- "msvcr120.dll", // VS2013
- NULL};
+ const char *InterestingDLLs[] = {
+ "kernel32.dll",
+ "msvcr110.dll", // VS2012
+ "msvcr120.dll", // VS2013
+ // NTDLL should go last as it exports some functions that we should override
+ // in the CRT [presumably only used internally].
+ "ntdll.dll", NULL
+ };
static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
if (!result[0]) {
for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
Added: compiler-rt/trunk/test/asan/TestCases/Windows/bind_io_completion_callback.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Windows/bind_io_completion_callback.cc?rev=232499&view=auto
==============================================================================
--- compiler-rt/trunk/test/asan/TestCases/Windows/bind_io_completion_callback.cc (added)
+++ compiler-rt/trunk/test/asan/TestCases/Windows/bind_io_completion_callback.cc Tue Mar 17 11:50:59 2015
@@ -0,0 +1,70 @@
+// Make sure we can throw exceptions from work items executed via
+// BindIoCompletionCallback.
+//
+// Clang doesn't support exceptions on Windows yet, so for the time being we
+// build this program in two parts: the code with exceptions is built with CL,
+// the rest is built with Clang. This represents the typical scenario when we
+// build a large project using "clang-cl -fallback -fsanitize=address".
+//
+// RUN: cl -c %s -Fo%t.obj
+// RUN: %clangxx_asan -o %t.exe %s %t.obj
+// RUN: %run %t.exe 2>&1 | FileCheck %s
+
+#include <windows.h>
+#include <stdio.h>
+
+void ThrowAndCatch();
+
+#if !defined(__clang__)
+__declspec(noinline)
+void Throw() {
+ fprintf(stderr, "Throw\n");
+// CHECK: Throw
+ throw 1;
+}
+
+void ThrowAndCatch() {
+ int local;
+ try {
+ Throw();
+ } catch(...) {
+ fprintf(stderr, "Catch\n");
+// CHECK: Catch
+ }
+}
+#else
+
+char buffer[65536];
+HANDLE done;
+OVERLAPPED ov;
+
+void CALLBACK completion_callback(DWORD error, DWORD bytesRead,
+ LPOVERLAPPED pov) {
+ ThrowAndCatch();
+ SetEvent(done);
+}
+
+int main(int argc, char **argv) {
+ done = CreateEvent(0, false, false, "job is done");
+ if (!done)
+ return 1;
+ HANDLE file = CreateFile(
+ argv[0], GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
+ NULL);
+ if (!file)
+ return 2;
+ if (!BindIoCompletionCallback(file, completion_callback, 0))
+ return 3;
+
+ if (!ReadFile(file, buffer, sizeof(buffer), NULL, &ov) &&
+ GetLastError() != ERROR_IO_PENDING)
+ return 4;
+
+ if (WAIT_OBJECT_0 != WaitForSingleObject(done, INFINITE))
+ return 5;
+ fprintf(stderr, "Done!\n");
+// CHECK: Done!
+}
+#endif
Added: compiler-rt/trunk/test/asan/TestCases/Windows/queue_user_work_item_report.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Windows/queue_user_work_item_report.cc?rev=232499&view=auto
==============================================================================
--- compiler-rt/trunk/test/asan/TestCases/Windows/queue_user_work_item_report.cc (added)
+++ compiler-rt/trunk/test/asan/TestCases/Windows/queue_user_work_item_report.cc Tue Mar 17 11:50:59 2015
@@ -0,0 +1,29 @@
+// RUN: %clang_cl_asan -O0 %s -Fe%t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+#include <windows.h>
+
+HANDLE done;
+
+DWORD CALLBACK work_item(LPVOID) {
+ int subscript = -1;
+ volatile char stack_buffer[42];
+ stack_buffer[subscript] = 42;
+// CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]]
+// CHECK: WRITE of size 1 at [[ADDR]] thread T1
+// CHECK: {{#0 .* work_item .*queue_user_work_item_report.cc}}:[[@LINE-3]]
+// CHECK: Address [[ADDR]] is located in stack of thread T1 at offset {{.*}} in frame
+// CHECK: work_item
+ SetEvent(done);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ done = CreateEvent(0, false, false, "job is done");
+ if (!done)
+ return 1;
+// CHECK-NOT: Thread T1 created
+ QueueUserWorkItem(&work_item, nullptr, 0);
+ if (WAIT_OBJECT_0 != WaitForSingleObject(done, INFINITE))
+ return 2;
+}
More information about the llvm-commits
mailing list