Merge "Do not access device maps."

am: a06e1c9eef

Change-Id: Id30b71a301953d8450bd66552a020437c5e91b94
This commit is contained in:
Christopher Ferris 2017-03-23 00:53:18 +00:00 committed by android-build-merger
commit cc99ba7ff5
7 changed files with 213 additions and 6 deletions

View file

@ -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) {

View file

@ -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);

View file

@ -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;

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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"