summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--debuggerd/libdebuggerd/tombstone.cpp2
-rw-r--r--include/backtrace/Backtrace.h6
-rw-r--r--include/backtrace/BacktraceMap.h4
-rw-r--r--libbacktrace/Backtrace.cpp11
-rw-r--r--libbacktrace/UnwindCurrent.cpp12
-rw-r--r--libbacktrace/UnwindPtrace.cpp18
-rw-r--r--libbacktrace/backtrace_test.cpp166
7 files changed, 213 insertions, 6 deletions
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index c23da4415..0c3844941 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -283,7 +283,7 @@ static void dump_stack_segment(
if (BacktraceMap::IsValid(map) && !map.name.empty()) {
line += " " + map.name;
uintptr_t offset = 0;
- std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset));
+ std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset, &map));
if (!func_name.empty()) {
line += " (" + func_name;
if (offset) {
diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h
index c896ab806..4f73a658c 100644
--- a/include/backtrace/Backtrace.h
+++ b/include/backtrace/Backtrace.h
@@ -104,8 +104,10 @@ public:
virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL) = 0;
// Get the function name and offset into the function given the pc.
- // If the string is empty, then no valid function name was found.
- virtual std::string GetFunctionName(uintptr_t pc, uintptr_t* offset);
+ // If the string is empty, then no valid function name was found,
+ // or the pc is not in any valid map.
+ virtual std::string GetFunctionName(uintptr_t pc, uintptr_t* offset,
+ const backtrace_map_t* map = NULL);
// Fill in the map data associated with the given pc.
virtual void FillInMap(uintptr_t pc, backtrace_map_t* map);
diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h
index df48dfe2b..8ab0dfabb 100644
--- a/include/backtrace/BacktraceMap.h
+++ b/include/backtrace/BacktraceMap.h
@@ -33,6 +33,10 @@
#include <string>
#include <vector>
+// Special flag to indicate a map is in /dev/. However, a map in
+// /dev/ashmem/... does not set this flag.
+static constexpr int PROT_DEVICE_MAP = 0x8000;
+
struct backtrace_map_t {
uintptr_t start = 0;
uintptr_t end = 0;
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 0d2e11bdf..4354f0b5a 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -52,7 +52,16 @@ Backtrace::~Backtrace() {
}
}
-std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
+std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset, const backtrace_map_t* map) {
+ backtrace_map_t map_value;
+ if (map == nullptr) {
+ FillInMap(pc, &map_value);
+ map = &map_value;
+ }
+ // If no map is found, or this map is backed by a device, then return nothing.
+ if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
+ return "";
+ }
std::string func_name = GetFunctionNameRaw(pc, offset);
return func_name;
}
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 4862d9dab..3c509e61a 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -127,7 +127,7 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucon
if (num_ignore_frames == 0) {
// GetFunctionName is an expensive call, only do it if we are
// keeping the frame.
- frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
+ frame->func_name = GetFunctionName(frame->pc, &frame->func_offset, &frame->map);
if (num_frames > 0) {
// Set the stack size for the previous frame.
backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
@@ -143,6 +143,16 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucon
frames_.resize(0);
}
}
+ // If the pc is in a device map, then don't try to step.
+ if (frame->map.flags & PROT_DEVICE_MAP) {
+ break;
+ }
+ // Verify the sp is not in a device map too.
+ backtrace_map_t map;
+ FillInMap(frame->sp, &map);
+ if (map.flags & PROT_DEVICE_MAP) {
+ break;
+ }
ret = unw_step (cursor.get());
} while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 5c73bd66b..42ac1bc07 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -136,12 +136,28 @@ bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
FillInMap(frame->pc, &frame->map);
- frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
+ frame->func_name = GetFunctionName(frame->pc, &frame->func_offset, &frame->map);
num_frames++;
+ // If the pc is in a device map, then don't try to step.
+ if (frame->map.flags & PROT_DEVICE_MAP) {
+ break;
+ }
} else {
+ // If the pc is in a device map, then don't try to step.
+ backtrace_map_t map;
+ FillInMap(pc, &map);
+ if (map.flags & PROT_DEVICE_MAP) {
+ break;
+ }
num_ignore_frames--;
}
+ // Verify the sp is not in a device map.
+ backtrace_map_t map;
+ FillInMap(sp, &map);
+ if (map.flags & PROT_DEVICE_MAP) {
+ break;
+ }
ret = unw_step (&cursor);
} while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 9ca373a7e..24e48cdb4 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -42,6 +42,7 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
+#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <cutils/atomic.h>
@@ -1436,6 +1437,171 @@ TEST(libbacktrace, remote_get_function_name_before_unwind) {
FinishRemoteProcess(pid);
}
+static void SetUcontextSp(uintptr_t sp, ucontext_t* ucontext) {
+#if defined(__arm__)
+ ucontext->uc_mcontext.arm_sp = sp;
+#elif defined(__aarch64__)
+ ucontext->uc_mcontext.sp = sp;
+#elif defined(__i386__)
+ ucontext->uc_mcontext.gregs[REG_ESP] = sp;
+#elif defined(__x86_64__)
+ ucontext->uc_mcontext.gregs[REG_RSP] = sp;
+#else
+ UNUSED(sp);
+ UNUSED(ucontext);
+ ASSERT_TRUE(false) << "Unsupported architecture";
+#endif
+}
+
+static void SetUcontextPc(uintptr_t pc, ucontext_t* ucontext) {
+#if defined(__arm__)
+ ucontext->uc_mcontext.arm_pc = pc;
+#elif defined(__aarch64__)
+ ucontext->uc_mcontext.pc = pc;
+#elif defined(__i386__)
+ ucontext->uc_mcontext.gregs[REG_EIP] = pc;
+#elif defined(__x86_64__)
+ ucontext->uc_mcontext.gregs[REG_RIP] = pc;
+#else
+ UNUSED(pc);
+ UNUSED(ucontext);
+ ASSERT_TRUE(false) << "Unsupported architecture";
+#endif
+}
+
+static void SetUcontextLr(uintptr_t lr, ucontext_t* ucontext) {
+#if defined(__arm__)
+ ucontext->uc_mcontext.arm_lr = lr;
+#elif defined(__aarch64__)
+ ucontext->uc_mcontext.regs[30] = lr;
+#elif defined(__i386__)
+ // The lr is on the stack.
+ ASSERT_TRUE(lr != 0);
+ ASSERT_TRUE(ucontext != nullptr);
+#elif defined(__x86_64__)
+ // The lr is on the stack.
+ ASSERT_TRUE(lr != 0);
+ ASSERT_TRUE(ucontext != nullptr);
+#else
+ UNUSED(lr);
+ UNUSED(ucontext);
+ ASSERT_TRUE(false) << "Unsupported architecture";
+#endif
+}
+
+static constexpr size_t DEVICE_MAP_SIZE = 1024;
+
+static void SetupDeviceMap(void** device_map) {
+ // Make sure that anything in a device map will result in fails
+ // to read.
+ android::base::unique_fd device_fd(open("/dev/zero", O_RDONLY | O_CLOEXEC));
+
+ *device_map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE, device_fd, 0);
+ ASSERT_TRUE(*device_map != MAP_FAILED);
+
+ // Make sure the map is readable.
+ ASSERT_EQ(0, reinterpret_cast<int*>(*device_map)[0]);
+}
+
+static void UnwindFromDevice(Backtrace* backtrace, void* device_map) {
+ uintptr_t device_map_uint = reinterpret_cast<uintptr_t>(device_map);
+
+ backtrace_map_t map;
+ backtrace->FillInMap(device_map_uint, &map);
+ // Verify the flag is set.
+ ASSERT_EQ(PROT_DEVICE_MAP, map.flags & PROT_DEVICE_MAP);
+
+ // Quick sanity checks.
+ size_t offset;
+ ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset));
+ ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset, &map));
+ ASSERT_EQ(std::string(""), backtrace->GetFunctionName(0, &offset));
+
+ uintptr_t cur_func_offset = reinterpret_cast<uintptr_t>(&test_level_one) + 1;
+ // Now verify the device map flag actually causes the function name to be empty.
+ backtrace->FillInMap(cur_func_offset, &map);
+ ASSERT_TRUE((map.flags & PROT_DEVICE_MAP) == 0);
+ ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset, &map));
+ map.flags |= PROT_DEVICE_MAP;
+ ASSERT_EQ(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset, &map));
+
+ ucontext_t ucontext;
+
+ // Create a context that has the pc in the device map, but the sp
+ // in a non-device map.
+ memset(&ucontext, 0, sizeof(ucontext));
+ SetUcontextSp(reinterpret_cast<uintptr_t>(&ucontext), &ucontext);
+ SetUcontextPc(device_map_uint, &ucontext);
+ SetUcontextLr(cur_func_offset, &ucontext);
+
+ ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
+
+ // The buffer should only be a single element.
+ ASSERT_EQ(1U, backtrace->NumFrames());
+ const backtrace_frame_data_t* frame = backtrace->GetFrame(0);
+ ASSERT_EQ(device_map_uint, frame->pc);
+ ASSERT_EQ(reinterpret_cast<uintptr_t>(&ucontext), frame->sp);
+
+ // Check what happens when skipping the first frame.
+ ASSERT_TRUE(backtrace->Unwind(1, &ucontext));
+ ASSERT_EQ(0U, backtrace->NumFrames());
+
+ // Create a context that has the sp in the device map, but the pc
+ // in a non-device map.
+ memset(&ucontext, 0, sizeof(ucontext));
+ SetUcontextSp(device_map_uint, &ucontext);
+ SetUcontextPc(cur_func_offset, &ucontext);
+ SetUcontextLr(cur_func_offset, &ucontext);
+
+ ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
+
+ // The buffer should only be a single element.
+ ASSERT_EQ(1U, backtrace->NumFrames());
+ frame = backtrace->GetFrame(0);
+ ASSERT_EQ(cur_func_offset, frame->pc);
+ ASSERT_EQ(device_map_uint, frame->sp);
+
+ // Check what happens when skipping the first frame.
+ ASSERT_TRUE(backtrace->Unwind(1, &ucontext));
+ ASSERT_EQ(0U, backtrace->NumFrames());
+}
+
+TEST(libbacktrace, unwind_disallow_device_map_local) {
+ void* device_map;
+ SetupDeviceMap(&device_map);
+
+ // Now create an unwind object.
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace);
+
+ UnwindFromDevice(backtrace.get(), device_map);
+
+ munmap(device_map, DEVICE_MAP_SIZE);
+}
+
+TEST(libbacktrace, unwind_disallow_device_map_remote) {
+ void* device_map;
+ SetupDeviceMap(&device_map);
+
+ // Fork a process to do a remote backtrace.
+ pid_t pid;
+ CreateRemoteProcess(&pid);
+
+ // Now create an unwind object.
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+
+ // TODO: Currently unwind from context doesn't work on remote
+ // unwind. Keep this test because the new unwinder should support
+ // this eventually, or we can delete this test.
+ // properly with unwind from context.
+ // UnwindFromDevice(backtrace.get(), device_map);
+
+ FinishRemoteProcess(pid);
+
+ munmap(device_map, DEVICE_MAP_SIZE);
+}
+
#if defined(ENABLE_PSS_TESTS)
#include "GetPss.h"