Handle the alternate signal stack correctly in android_unsafe_frame_pointer_chase. am: 91740684c2

Original change: https://googleplex-android-review.googlesource.com/c/platform/bionic/+/11720548

Change-Id: I32c401d5d99bf2cfb046b65c688b1ac140fef438
diff --git a/libc/bionic/android_unsafe_frame_pointer_chase.cpp b/libc/bionic/android_unsafe_frame_pointer_chase.cpp
index 0fb086e..e25867b 100644
--- a/libc/bionic/android_unsafe_frame_pointer_chase.cpp
+++ b/libc/bionic/android_unsafe_frame_pointer_chase.cpp
@@ -57,6 +57,12 @@
 
   auto begin = reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
   uintptr_t end = __get_thread()->stack_top;
+
+  stack_t ss;
+  if (sigaltstack(nullptr, &ss) == 0 && (ss.ss_flags & SS_ONSTACK)) {
+    end = reinterpret_cast<uintptr_t>(ss.ss_sp) + ss.ss_size;
+  }
+
   size_t num_frames = 0;
   while (1) {
     auto* frame = reinterpret_cast<frame_record*>(begin);
diff --git a/tests/android_unsafe_frame_pointer_chase_test.cpp b/tests/android_unsafe_frame_pointer_chase_test.cpp
index dd04c33..7fa50e1 100644
--- a/tests/android_unsafe_frame_pointer_chase_test.cpp
+++ b/tests/android_unsafe_frame_pointer_chase_test.cpp
@@ -18,6 +18,8 @@
 
 #if defined(__BIONIC__)
 
+#include <sys/mman.h>
+
 #include "platform/bionic/android_unsafe_frame_pointer_chase.h"
 
 // Prevent tail calls inside recurse.
@@ -72,21 +74,25 @@
   EXPECT_TRUE(CheckFrames(frames, size));
 }
 
-static void *BacktraceThread(void *) {
+static const char* tester_func() {
   size_t size = recurse(kNumFrames, 0, 0);
 
   uintptr_t frames[kNumFrames + 2];
   size_t size2 = recurse(kNumFrames, frames, kNumFrames + 2);
   if (size2 != size) {
-    return (void*)"size2 != size";
+    return "size2 != size";
   }
 
   if (!CheckFrames(frames, size)) {
-    return (void*)"CheckFrames failed";
+    return "CheckFrames failed";
   }
   return nullptr;
 }
 
+static void* BacktraceThread(void*) {
+  return (void*)tester_func();
+}
+
 TEST(android_unsafe_frame_pointer_chase, pthread) {
   pthread_t t;
   ASSERT_EQ(0, pthread_create(&t, nullptr, BacktraceThread, nullptr));
@@ -95,4 +101,58 @@
   EXPECT_EQ(nullptr, reinterpret_cast<char*>(retval));
 }
 
+static bool g_handler_called;
+static const char* g_handler_tester_result;
+
+static void BacktraceHandler(int) {
+  g_handler_called = true;
+  g_handler_tester_result = tester_func();
+}
+
+static constexpr size_t kStackSize = 16384;
+
+static void* SignalBacktraceThread(void* sp) {
+  stack_t ss;
+  ss.ss_sp = sp;
+  ss.ss_flags = 0;
+  ss.ss_size = kStackSize;
+  sigaltstack(&ss, nullptr);
+
+  struct sigaction s = {};
+  s.sa_handler = BacktraceHandler;
+  s.sa_flags = SA_ONSTACK;
+  sigaction(SIGRTMIN, &s, nullptr);
+
+  raise(SIGRTMIN);
+  return nullptr;
+}
+
+TEST(android_unsafe_frame_pointer_chase, sigaltstack) {
+  // Create threads where the alternate stack appears both after and before the regular stack, and
+  // call android_unsafe_frame_pointer_chase from a signal handler. Without handling for the
+  // alternate signal stack, this would cause false negatives or potential false positives in the
+  // android_unsafe_frame_pointer_chase function.
+  void* stacks =
+      mmap(nullptr, kStackSize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+
+  for (unsigned i = 0; i != 2; ++i) {
+    pthread_t t;
+    pthread_attr_t attr;
+    ASSERT_EQ(0, pthread_attr_init(&attr));
+    ASSERT_EQ(0, pthread_attr_setstack(&attr, reinterpret_cast<char*>(stacks) + kStackSize * i,
+                                       kStackSize));
+
+    ASSERT_EQ(0, pthread_create(&t, &attr, SignalBacktraceThread,
+                                reinterpret_cast<char*>(stacks) + kStackSize * (1 - i)));
+    void* retval;
+    ASSERT_EQ(0, pthread_join(t, &retval));
+
+    EXPECT_TRUE(g_handler_called);
+    EXPECT_EQ(nullptr, g_handler_tester_result);
+    g_handler_called = false;
+  }
+
+  munmap(stacks, kStackSize * 2);
+}
+
 #endif // __BIONIC__