[compiler-rt] [ASan][Windows] Synchronizing ASAN init on Windows (PR #71833)

Zack Johnson via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 16 07:14:27 PST 2023


https://github.com/zacklj89 updated https://github.com/llvm/llvm-project/pull/71833

>From 441bebdb0be456eba70a24f43cbd9a0d1c8b0b62 Mon Sep 17 00:00:00 2001
From: Zachary Johnson <zajohnson at microsoft.com>
Date: Thu, 9 Nov 2023 15:13:24 -0500
Subject: [PATCH 1/2] [asan] Fixing initialization synchronization on Windows

---
 compiler-rt/lib/asan/asan_internal.h | 13 +++++++
 compiler-rt/lib/asan/asan_rtl.cpp    | 51 ++++++++++++++++++++++++----
 compiler-rt/lib/asan/asan_thread.cpp | 23 +++++++++++++
 3 files changed, 81 insertions(+), 6 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_internal.h b/compiler-rt/lib/asan/asan_internal.h
index e2b1e9800f5be62..f98cbd5da96bc8c 100644
--- a/compiler-rt/lib/asan/asan_internal.h
+++ b/compiler-rt/lib/asan/asan_internal.h
@@ -130,6 +130,19 @@ void InstallAtExitCheckLeaks();
   if (&__asan_on_error) \
   __asan_on_error()
 
+// Depending on the loading thread and when ASAN is loaded on Windows,
+// race conditions can appear causing incorrect states or internal check
+// failures.
+//
+// From a multithreaded managed environment, if an ASAN instrumented dll
+// is loading on a spawned thread, an intercepted function may be called on
+// multiple threads while ASAN is still in the process of initialization. This
+// can also cause the ASAN thread registry to create the "main" thread after
+// another thread, resulting in a TID != 0.
+//
+// Two threads can also race to initialize ASAN, resulting in either incorrect
+// state or internal check failures for init already running.
+//
 bool AsanInited();
 bool AsanInitIsRunning();  // Used to avoid infinite recursion in __asan_init().
 extern bool replace_intrin_cached;
diff --git a/compiler-rt/lib/asan/asan_rtl.cpp b/compiler-rt/lib/asan/asan_rtl.cpp
index d1e7856973b43b3..ade77d68f36d56d 100644
--- a/compiler-rt/lib/asan/asan_rtl.cpp
+++ b/compiler-rt/lib/asan/asan_rtl.cpp
@@ -71,16 +71,54 @@ static void CheckUnwind() {
 }
 
 // -------------------------- Globals --------------------- {{{1
-static int asan_inited = 0;
-static int asan_init_is_running = 0;
+#if SANITIZER_WINDOWS
+atomic_uint8_t asan_inited{0};
+atomic_uint8_t asan_init_is_running{0};
+#else
+int asan_inited = 0;
+int asan_init_is_running = 0;
+#endif
 
-void SetAsanInited(u32 val) { asan_inited = val; }
+void SetAsanInited(u32 val) {
+#if SANITIZER_WINDOWS
+  atomic_store(&asan_inited, val, memory_order_release);
+#else
+  asan_inited = val;
+#endif
+}
 
-void SetAsanInitIsRunning(u32 val) { asan_init_is_running = val; }
+void SetAsanInitIsRunning(u32 val) {
+#if SANITIZER_WINDOWS
+  atomic_store(&asan_init_is_running, val, memory_order_release);
+#else
+  asan_init_is_running = val;
+#endif
+}
 
-bool AsanInited() { return asan_inited == 1; }
+bool AsanInited() {
+#if SANITIZER_WINDOWS
+  return atomic_load(&asan_inited, memory_order_acquire) == 1;
+#else
+  return asan_inited == 1;
+#endif
+}
 
-bool AsanInitIsRunning() { return asan_init_is_running == 1; }
+bool AsanInitIsRunning() {
+#if SANITIZER_WINDOWS
+  return atomic_load(&asan_init_is_running, memory_order_acquire) == 1;
+#else
+  return asan_init_is_running == 1;
+#endif
+}
+
+void CheckAsanInitRunning() {
+#if SANITIZER_WINDOWS
+  while (AsanInitIsRunning()) {
+    // If ASAN is initializing on another thread, wait for it to finish.
+  }
+#endif
+  return;
+}
 
 bool replace_intrin_cached;
 
@@ -391,6 +429,7 @@ void PrintAddressSpaceLayout() {
 }
 
 static void AsanInitInternal() {
+  CheckAsanInitRunning();
   if (LIKELY(AsanInited()))
     return;
   SanitizerToolName = "AddressSanitizer";
diff --git a/compiler-rt/lib/asan/asan_thread.cpp b/compiler-rt/lib/asan/asan_thread.cpp
index 8798968947e82e6..88d526069352bcb 100644
--- a/compiler-rt/lib/asan/asan_thread.cpp
+++ b/compiler-rt/lib/asan/asan_thread.cpp
@@ -27,6 +27,10 @@ namespace __asan {
 
 // AsanThreadContext implementation.
 
+#if SANITIZER_WINDOWS
+static atomic_uint8_t main_thread_created{0};
+#endif
+
 void AsanThreadContext::OnCreated(void *arg) {
   CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs *>(arg);
   if (args->stack)
@@ -93,6 +97,11 @@ AsanThreadContext *GetThreadContextByTidLocked(u32 tid) {
 AsanThread *AsanThread::Create(const void *start_data, uptr data_size,
                                u32 parent_tid, StackTrace *stack,
                                bool detached) {
+#if SANITIZER_WINDOWS
+  while (atomic_load(&main_thread_created, memory_order_acquire) == 0) {
+    // If another thread is trying to be created before the main thread, wait.
+  }
+#endif
   uptr PageSize = GetPageSizeCached();
   uptr size = RoundUpTo(sizeof(AsanThread), PageSize);
   AsanThread *thread = (AsanThread *)MmapOrDie(size, __func__);
@@ -288,11 +297,25 @@ void AsanThread::ThreadStart(tid_t os_id) {
 }
 
 AsanThread *CreateMainThread() {
+// Depending on the loading thread, specifically in managed scenarios, the main
+// thread can be created after other threads on Windows. This ensures we start
+// the main thread before those threads.
+#  if SANITIZER_WINDOWS
+  uptr PageSize = GetPageSizeCached();
+  uptr size = RoundUpTo(sizeof(AsanThread), PageSize);
+  AsanThread *main_thread = (AsanThread *)MmapOrDie(size, __func__);
+  AsanThreadContext::CreateThreadContextArgs args = {main_thread, nullptr};
+  asanThreadRegistry().CreateThread(0, true, kMainTid, &args);
+  SetCurrentThread(main_thread);
+  main_thread->ThreadStart(internal_getpid());
+  atomic_store(&main_thread_created, 1, memory_order_release);
+#  else
   AsanThread *main_thread = AsanThread::Create(
       /* parent_tid */ kMainTid,
       /* stack */ nullptr, /* detached */ true);
   SetCurrentThread(main_thread);
   main_thread->ThreadStart(internal_getpid());
+#  endif
   return main_thread;
 }
 

>From 683139e76cc9f31d7f9f43d804f8e788d3679b64 Mon Sep 17 00:00:00 2001
From: Zachary Johnson <zajohnson at microsoft.com>
Date: Thu, 16 Nov 2023 10:14:11 -0500
Subject: [PATCH 2/2] adding yields

---
 compiler-rt/lib/asan/asan_rtl.cpp    | 2 +-
 compiler-rt/lib/asan/asan_thread.cpp | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/compiler-rt/lib/asan/asan_rtl.cpp b/compiler-rt/lib/asan/asan_rtl.cpp
index ade77d68f36d56d..092ddf15d5a3db2 100644
--- a/compiler-rt/lib/asan/asan_rtl.cpp
+++ b/compiler-rt/lib/asan/asan_rtl.cpp
@@ -115,9 +115,9 @@ void CheckAsanInitRunning() {
 #if SANITIZER_WINDOWS
   while (AsanInitIsRunning()) {
     // If ASAN is initializing on another thread, wait for it to finish.
+    internal_sched_yield();
   }
 #endif
-  return;
 }
 
 bool replace_intrin_cached;
diff --git a/compiler-rt/lib/asan/asan_thread.cpp b/compiler-rt/lib/asan/asan_thread.cpp
index 88d526069352bcb..dc0ad2caf3bbd10 100644
--- a/compiler-rt/lib/asan/asan_thread.cpp
+++ b/compiler-rt/lib/asan/asan_thread.cpp
@@ -100,6 +100,7 @@ AsanThread *AsanThread::Create(const void *start_data, uptr data_size,
 #if SANITIZER_WINDOWS
   while (atomic_load(&main_thread_created, memory_order_acquire) == 0) {
     // If another thread is trying to be created before the main thread, wait.
+    internal_sched_yield();
   }
 #endif
   uptr PageSize = GetPageSizeCached();



More information about the llvm-commits mailing list