aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Collingbourne <pcc@google.com>2020-02-26 19:01:13 -0800
committerPeter Collingbourne <pcc@google.com>2020-06-03 10:23:29 -0700
commit91740684c29535448db6cdb691e84f7a86a69ba3 (patch)
treeca6e5405a832ce4390bc51cdcbfdf380c304afe3
parenteb6c7abdf9bec246a85ef0826c3739753ff3bf2c (diff)
downloadbionic-91740684c29535448db6cdb691e84f7a86a69ba3.tar.gz
Handle the alternate signal stack correctly in android_unsafe_frame_pointer_chase.
If an alternate signal stack is set and the frame record is in bounds of that stack, we need to use the top of the alternate signal stack for bounds checking rather than the normal stack. Bug: 150215618 Change-Id: I78b760d61b27da44f8e0cfee3fe94a791011fe58 Merged-In: I78b760d61b27da44f8e0cfee3fe94a791011fe58
-rw-r--r--libc/bionic/android_unsafe_frame_pointer_chase.cpp6
-rw-r--r--tests/android_unsafe_frame_pointer_chase_test.cpp66
2 files changed, 69 insertions, 3 deletions
diff --git a/libc/bionic/android_unsafe_frame_pointer_chase.cpp b/libc/bionic/android_unsafe_frame_pointer_chase.cpp
index 0fb086e49..e25867b0e 100644
--- a/libc/bionic/android_unsafe_frame_pointer_chase.cpp
+++ b/libc/bionic/android_unsafe_frame_pointer_chase.cpp
@@ -57,6 +57,12 @@ __attribute__((no_sanitize("address", "hwaddress"))) size_t android_unsafe_frame
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 dd04c3377..7fa50e149 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 @@ TEST(android_unsafe_frame_pointer_chase, main_thread) {
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 @@ TEST(android_unsafe_frame_pointer_chase, pthread) {
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__