[libasan] Remove 4Mb stack limit for swapcontext unpoisoning
authorIvan Trofimov <i.trofimow@yandex.ru>
Mon, 11 Jul 2022 17:12:31 +0000 (10:12 -0700)
committerVitaly Buka <vitalybuka@google.com>
Sat, 23 Jul 2022 00:37:44 +0000 (17:37 -0700)
Reviewed By: vitalybuka, eugenis

Differential Revision: https://reviews.llvm.org/D129219

compiler-rt/lib/asan/asan_interceptors.cpp
compiler-rt/test/asan/TestCases/Linux/swapcontext_annotation.cpp

index ff6ffeca36d5dd29078a9a0c17ef2a79423d8917..817008253fc0ef55018041afd38b414ada9542bf 100644 (file)
@@ -243,14 +243,18 @@ DEFINE_REAL_PTHREAD_FUNCTIONS
 
 #if ASAN_INTERCEPT_SWAPCONTEXT
 static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) {
+  // Only clear if we know the stack. This should be true only for contexts
+  // created with makecontext().
+  if (!ssize)
+    return;
   // Align to page size.
   uptr PageSize = GetPageSizeCached();
   uptr bottom = RoundDownTo(stack, PageSize);
+  if (!AddrIsInMem(bottom))
+    return;
   ssize += stack - bottom;
   ssize = RoundUpTo(ssize, PageSize);
-  static const uptr kMaxSaneContextStackSize = 1 << 22;  // 4 Mb
-  if (AddrIsInMem(bottom) && ssize && ssize <= kMaxSaneContextStackSize)
-    PoisonShadow(bottom, ssize, 0);
+  PoisonShadow(bottom, ssize, 0);
 }
 
 INTERCEPTOR(int, getcontext, struct ucontext_t *ucp) {
index fd5ae17c4d0a50afbea23e1a975d6b5efad73d18..5753f1397963664b06beda99f1c2f35e72c1eed5 100644 (file)
@@ -146,9 +146,61 @@ int Run(int arg, int mode, char *child_stack) {
   return child_stack[arg];
 }
 
+ucontext_t orig_huge_stack_context;
+ucontext_t child_huge_stack_context;
+
+// There used to be a limitation for stack unpoisoning (size <= 4Mb), check that it's gone.
+const int kHugeStackSize = 1 << 23;
+
+void ChildHugeStack() {
+  __sanitizer_finish_switch_fiber(nullptr, &main_thread_stack,
+                                  &main_thread_stacksize);
+  char x[32] = {0}; // Stack gets poisoned.
+  __sanitizer_start_switch_fiber(nullptr, main_thread_stack,
+                                 main_thread_stacksize);
+  if (swapcontext(&child_huge_stack_context, &orig_huge_stack_context) < 0) {
+    perror("swapcontext");
+    _exit(1);
+  }
+}
+
+void DoRunHugeStack(char *child_stack) {
+  getcontext(&child_huge_stack_context);
+  child_huge_stack_context.uc_stack.ss_sp = child_stack;
+  child_huge_stack_context.uc_stack.ss_size = kHugeStackSize;
+  makecontext(&child_huge_stack_context, (void (*)())ChildHugeStack, 0);
+  void *fake_stack_save;
+  __sanitizer_start_switch_fiber(&fake_stack_save,
+                                 child_huge_stack_context.uc_stack.ss_sp,
+                                 child_huge_stack_context.uc_stack.ss_size);
+  if (swapcontext(&orig_huge_stack_context, &child_huge_stack_context) < 0) {
+    perror("swapcontext");
+    _exit(1);
+  }
+  __sanitizer_finish_switch_fiber(
+      fake_stack_save, (const void **)&child_huge_stack_context.uc_stack.ss_sp,
+      &child_huge_stack_context.uc_stack.ss_size);
+  for (int i = 0; i < kHugeStackSize; ++i) {
+    child_stack[i] = i;
+  }
+}
+
+void RunHugeStack() {
+  const int run_offset = 1 << 14;
+  char *heap = new char[kHugeStackSize + run_offset + 1];
+  DoRunHugeStack(heap);
+  DoRunHugeStack(heap + run_offset);
+  DoRunHugeStack(heap);
+  delete[] heap;
+}
+
 void handler(int sig) { CallNoReturn(); }
 
 int main(int argc, char **argv) {
+  // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext
+  // CHECK-NOT: ASan is ignoring requested __asan_handle_no_return
+  RunHugeStack();
+
   // set up a signal that will spam and trigger __asan_handle_no_return at
   // tricky moments
   struct sigaction act = {};
@@ -170,7 +222,6 @@ int main(int argc, char **argv) {
   char *heap = new char[kStackSize + 1];
   next_child_stack = new char[kStackSize + 1];
   char stack[kStackSize + 1];
-  // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext
   int ret = 0;
   // CHECK-NOT: ASan is ignoring requested __asan_handle_no_return
   for (unsigned int i = 0; i < 30; ++i) {