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__