Merge "Do not access device maps."
am: a06e1c9eef
Change-Id: Id30b71a301953d8450bd66552a020437c5e91b94
This commit is contained in:
commit
cc99ba7ff5
7 changed files with 213 additions and 6 deletions
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Reference in a new issue