[compiler-rt] 9be8892 - [asan] Fix GC of FakeFrames

Vitaly Buka via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 9 20:47:39 PDT 2023


Author: Vitaly Buka
Date: 2023-08-09T20:47:23-07:00
New Revision: 9be8892908d49c19fd6c9fc930d0f41276c3e345

URL: https://github.com/llvm/llvm-project/commit/9be8892908d49c19fd6c9fc930d0f41276c3e345
DIFF: https://github.com/llvm/llvm-project/commit/9be8892908d49c19fd6c9fc930d0f41276c3e345.diff

LOG: [asan] Fix GC of FakeFrames

When FakeStack GC from altstack, it may see default stack on lower
addressed and incorectly disard all frames.

Fixes bug exposed by D153536.

Reviewed By: kstoimenov

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

Added: 
    compiler-rt/test/asan/TestCases/Posix/fake_stack_gc.cpp

Modified: 
    compiler-rt/lib/asan/asan_fake_stack.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/asan/asan_fake_stack.cpp b/compiler-rt/lib/asan/asan_fake_stack.cpp
index 72d1920579e997..8680bc6d6630e2 100644
--- a/compiler-rt/lib/asan/asan_fake_stack.cpp
+++ b/compiler-rt/lib/asan/asan_fake_stack.cpp
@@ -133,6 +133,12 @@ void FakeStack::HandleNoReturn() {
   needs_gc_ = true;
 }
 
+// Hack: The statement below is not true if we take into account sigaltstack or
+// makecontext. It should be possible to make GC to discard wrong stack frame if
+// we use these tools. For now, let's support the simplest case and allow GC to
+// discard only frames from the default stack, assuming there is no buffer on
+// the stack which is used for makecontext or sigaltstack.
+//
 // When throw, longjmp or some such happens we don't call OnFree() and
 // as the result may leak one or more fake frames, but the good news is that
 // we are notified about all such events by HandleNoReturn().
@@ -140,6 +146,14 @@ void FakeStack::HandleNoReturn() {
 // We do it based on their 'real_stack' values -- everything that is lower
 // than the current real_stack is garbage.
 NOINLINE void FakeStack::GC(uptr real_stack) {
+  AsanThread *curr_thread = GetCurrentThread();
+  if (!curr_thread)
+    return;  // Try again when we have a thread.
+  auto top = curr_thread->stack_top();
+  auto bottom = curr_thread->stack_bottom();
+  if (real_stack < top || real_stack > bottom)
+    return;  // Not the default stack.
+
   for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) {
     u8 *flags = GetFlags(stack_size_log(), class_id);
     for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n;
@@ -147,8 +161,12 @@ NOINLINE void FakeStack::GC(uptr real_stack) {
       if (flags[i] == 0) continue;  // not allocated.
       FakeFrame *ff = reinterpret_cast<FakeFrame *>(
           GetFrame(stack_size_log(), class_id, i));
-      if (ff->real_stack < real_stack) {
+      // GC only on the default stack.
+      if (ff->real_stack < real_stack && ff->real_stack >= top) {
         flags[i] = 0;
+        // Poison the frame, so the any access will be reported as UAR.
+        SetShadow(reinterpret_cast<uptr>(ff), BytesInSizeClass(class_id),
+                  class_id, kMagic8);
       }
     }
   }

diff  --git a/compiler-rt/test/asan/TestCases/Posix/fake_stack_gc.cpp b/compiler-rt/test/asan/TestCases/Posix/fake_stack_gc.cpp
new file mode 100644
index 00000000000000..57c07187b1b138
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Posix/fake_stack_gc.cpp
@@ -0,0 +1,92 @@
+// RUN: %clangxx_asan -O0 -pthread %s -o %t && %env_asan_opts=use_sigaltstack=0 %run not --crash %t 2>&1 | FileCheck %s
+
+// Check that fake stack does not discard frames on the main stack, when GC is
+// triggered from high alt stack.
+
+#include <algorithm>
+#include <assert.h>
+#include <csignal>
+#include <cstdint>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+const size_t kStackSize = 0x100000;
+
+int *on_thread;
+int *p;
+
+template <size_t N> void Fn() {
+  int t[N];
+  p = t;
+  if constexpr (N > 1)
+    Fn<N - 1>();
+}
+
+static void Handler(int signo) {
+  fprintf(stderr, "Handler Frame:%p\n", __builtin_frame_address(0));
+
+  // Trigger GC and create a lot of frame to reuse "Thread" frame if it was
+  // discarded.
+  for (int i = 0; i < 1000; ++i)
+    Fn<1000>();
+  // If we discarder and reused "Thread" frame, the next line will crash with
+  // false report.
+  *on_thread = 10;
+  fprintf(stderr, "SUCCESS\n");
+  // CHECK: SUCCESS
+}
+
+void *Thread(void *arg) {
+  fprintf(stderr, "Thread Frame:%p\n", __builtin_frame_address(0));
+  stack_t stack = {
+      .ss_sp = arg,
+      .ss_flags = 0,
+      .ss_size = kStackSize,
+  };
+  assert(sigaltstack(&stack, nullptr) == 0);
+
+  struct sigaction sa = {};
+  sa.sa_handler = Handler;
+  sa.sa_flags = SA_ONSTACK;
+  sigaction(SIGABRT, &sa, nullptr);
+
+  // Store pointer to the local var, so we can access this frame from the signal
+  // handler when the frame is still alive.
+  int n;
+  on_thread = &n;
+
+  // Abort should schedule FakeStack GC and call handler on alt stack.
+  abort();
+}
+
+int main(void) {
+  // Allocate main and alt stack for future thread.
+  void *main_stack = malloc(kStackSize);
+  void *alt_stack = malloc(kStackSize);
+
+  // Pick the lower stack as the main stack, as we want to trigger GC in
+  // FakeStack from alt stack in a such way that main stack is allocated below.
+  if ((uintptr_t)main_stack > (uintptr_t)alt_stack)
+    std::swap(alt_stack, main_stack);
+
+  pthread_attr_t attr;
+  assert(pthread_attr_init(&attr) == 0);
+  assert(pthread_attr_setstack(&attr, main_stack, kStackSize) == 0);
+
+  fprintf(stderr, "main_stack: %p-%p\n", main_stack,
+          (char *)main_stack + kStackSize);
+  fprintf(stderr, "alt_stack: %p-%p\n", alt_stack,
+          (char *)alt_stack + kStackSize);
+
+  pthread_t tid;
+  assert(pthread_create(&tid, &attr, Thread, alt_stack) == 0);
+
+  pthread_join(tid, nullptr);
+
+  free(main_stack);
+  free(alt_stack);
+
+  return 0;
+}


        


More information about the llvm-commits mailing list