Snap for 4799153 from 428a6ffaf8 to pi-release

Change-Id: I8b50c825ba0cdcaedd4729b759cecf5ee3d48a4c
This commit is contained in:
android-build-team Robot 2018-05-23 07:28:01 +00:00
commit a8992075c0
19 changed files with 330 additions and 98 deletions

View File

@ -187,6 +187,10 @@ runs the service.
seclabel or computed based on the service executable file security context.
For native executables see libcutils android\_get\_control\_socket().
`enter_namespace <type> <path>`
> Enters the namespace of type _type_ located at _path_. Only network namespaces are supported with
_type_ set to "net". Note that only one namespace of a given _type_ may be entered.
`file <path> <type>`
> Open a file path and pass its fd to the launched process. _type_ must be
"r", "w" or "rw". For native executables see libcutils

View File

@ -95,6 +95,11 @@ uint32_t (*property_set)(const std::string& name, const std::string& value) = In
void CreateSerializedPropertyInfo();
struct PropertyAuditData {
const ucred* cr;
const char* name;
};
void property_init() {
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
@ -111,7 +116,7 @@ static bool CheckMacPerms(const std::string& name, const char* target_context,
return false;
}
property_audit_data audit_data;
PropertyAuditData audit_data;
audit_data.name = name.c_str();
audit_data.cr = &cr;
@ -388,6 +393,35 @@ class SocketConnection {
DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
};
bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr) {
// We check the legacy method first but these properties are dontaudit, so we only log an audit
// if the newer method fails as well. We only do this with the legacy ctl. properties.
if (name == "ctl.start" || name == "ctl.stop" || name == "ctl.restart") {
// The legacy permissions model is that ctl. properties have their name ctl.<action> and
// their value is the name of the service to apply that action to. Permissions for these
// actions are based on the service, so we must create a fake name of ctl.<service> to
// check permissions.
auto control_string_legacy = "ctl." + value;
const char* target_context_legacy = nullptr;
const char* type_legacy = nullptr;
property_info_area->GetPropertyInfo(control_string_legacy.c_str(), &target_context_legacy,
&type_legacy);
if (CheckMacPerms(control_string_legacy, target_context_legacy, source_context.c_str(), cr)) {
return true;
}
}
auto control_string_full = name + "$" + value;
const char* target_context_full = nullptr;
const char* type_full = nullptr;
property_info_area->GetPropertyInfo(control_string_full.c_str(), &target_context_full,
&type_full);
return CheckMacPerms(control_string_full, target_context_full, source_context.c_str(), cr);
}
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error) {
@ -397,15 +431,9 @@ uint32_t HandlePropertySet(const std::string& name, const std::string& value,
}
if (StartsWith(name, "ctl.")) {
// ctl. properties have their name ctl.<action> and their value is the name of the service
// to apply that action to. Permissions for these actions are based on the service, so we
// must create a fake name of ctl.<service> to check permissions.
auto control_string = "ctl." + value;
const char* target_context = nullptr;
const char* type = nullptr;
property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) {
*error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str());
if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
*error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
value.c_str());
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
@ -737,7 +765,7 @@ void load_system_props() {
}
static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
auto* d = reinterpret_cast<PropertyAuditData*>(data);
if (!d || !d->name || !d->cr) {
LOG(ERROR) << "AuditCallback invoked with null data arguments!";

View File

@ -24,11 +24,6 @@
namespace android {
namespace init {
struct property_audit_data {
const ucred* cr;
const char* name;
};
extern uint32_t (*property_set)(const std::string& name, const std::string& value);
uint32_t HandlePropertySet(const std::string& name, const std::string& value,

View File

@ -34,6 +34,7 @@
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <hidl-util/FQName.h>
#include <processgroup/processgroup.h>
#include <selinux/selinux.h>
@ -59,13 +60,13 @@ using android::base::Join;
using android::base::ParseInt;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::unique_fd;
using android::base::WriteStringToFile;
namespace android {
namespace init {
static Result<std::string> ComputeContextFromExecutable(std::string& service_name,
const std::string& service_path) {
static Result<std::string> ComputeContextFromExecutable(const std::string& service_path) {
std::string computed_context;
char* raw_con = nullptr;
@ -101,36 +102,49 @@ static Result<std::string> ComputeContextFromExecutable(std::string& service_nam
return computed_context;
}
static void SetUpPidNamespace(const std::string& service_name) {
Result<Success> Service::SetUpMountNamespace() const {
constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
// It's OK to LOG(FATAL) in this function since it's running in the first
// child process.
// Recursively remount / as slave like zygote does so unmounting and mounting /proc
// doesn't interfere with the parent namespace's /proc mount. This will also
// prevent any other mounts/unmounts initiated by the service from interfering
// with the parent namespace but will still allow mount events from the parent
// namespace to propagate to the child.
if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
PLOG(FATAL) << "couldn't remount(/) recursively as slave for " << service_name;
}
// umount() then mount() /proc.
// Note that it is not sufficient to mount with MS_REMOUNT.
if (umount("/proc") == -1) {
PLOG(FATAL) << "couldn't umount(/proc) for " << service_name;
}
if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
PLOG(FATAL) << "couldn't mount(/proc) for " << service_name;
return ErrnoError() << "Could not remount(/) recursively as slave";
}
if (prctl(PR_SET_NAME, service_name.c_str()) == -1) {
PLOG(FATAL) << "couldn't set name for " << service_name;
// umount() then mount() /proc and/or /sys
// Note that it is not sufficient to mount with MS_REMOUNT.
if (namespace_flags_ & CLONE_NEWPID) {
if (umount("/proc") == -1) {
return ErrnoError() << "Could not umount(/proc)";
}
if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
return ErrnoError() << "Could not mount(/proc)";
}
}
bool remount_sys = std::any_of(namespaces_to_enter_.begin(), namespaces_to_enter_.end(),
[](const auto& entry) { return entry.first == CLONE_NEWNET; });
if (remount_sys) {
if (umount2("/sys", MNT_DETACH) == -1) {
return ErrnoError() << "Could not umount(/sys)";
}
if (mount("", "/sys", "sys", kSafeFlags, "") == -1) {
return ErrnoError() << "Could not mount(/sys)";
}
}
return Success();
}
Result<Success> Service::SetUpPidNamespace() const {
if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
return ErrnoError() << "Could not set name";
}
pid_t child_pid = fork();
if (child_pid == -1) {
PLOG(FATAL) << "couldn't fork init inside the PID namespace for " << service_name;
return ErrnoError() << "Could not fork init inside the PID namespace";
}
if (child_pid > 0) {
@ -153,6 +167,20 @@ static void SetUpPidNamespace(const std::string& service_name) {
}
_exit(WEXITSTATUS(init_exitstatus));
}
return Success();
}
Result<Success> Service::EnterNamespaces() const {
for (const auto& [nstype, path] : namespaces_to_enter_) {
auto fd = unique_fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
if (!fd) {
return ErrnoError() << "Could not open namespace at " << path;
}
if (setns(fd, nstype) == -1) {
return ErrnoError() << "Could not setns() namespace at " << path;
}
}
return Success();
}
static bool ExpandArgsAndExecv(const std::vector<std::string>& args) {
@ -418,6 +446,20 @@ Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
return Success();
}
Result<Success> Service::ParseEnterNamespace(const std::vector<std::string>& args) {
if (args[1] != "net") {
return Error() << "Init only supports entering network namespaces";
}
if (!namespaces_to_enter_.empty()) {
return Error() << "Only one network namespace may be entered";
}
// Network namespaces require that /sys is remounted, otherwise the old adapters will still be
// present. Therefore, they also require mount namespaces.
namespace_flags_ |= CLONE_NEWNS;
namespaces_to_enter_.emplace_back(CLONE_NEWNET, args[2]);
return Success();
}
Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
auto gid = DecodeUid(args[1]);
if (!gid) {
@ -682,6 +724,8 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
{"console", {0, 1, &Service::ParseConsole}},
{"critical", {0, 0, &Service::ParseCritical}},
{"disabled", {0, 0, &Service::ParseDisabled}},
{"enter_namespace",
{2, 2, &Service::ParseEnterNamespace}},
{"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
{"interface", {2, 2, &Service::ParseInterface}},
{"ioprio", {2, 2, &Service::ParseIoprio}},
@ -783,7 +827,7 @@ Result<Success> Service::Start() {
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
auto result = ComputeContextFromExecutable(name_, args_[0]);
auto result = ComputeContextFromExecutable(args_[0]);
if (!result) {
return result.error();
}
@ -802,10 +846,24 @@ Result<Success> Service::Start() {
if (pid == 0) {
umask(077);
if (auto result = EnterNamespaces(); !result) {
LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
}
if (namespace_flags_ & CLONE_NEWNS) {
if (auto result = SetUpMountNamespace(); !result) {
LOG(FATAL) << "Service '" << name_
<< "' could not set up mount namespace: " << result.error();
}
}
if (namespace_flags_ & CLONE_NEWPID) {
// This will fork again to run an init process inside the PID
// namespace.
SetUpPidNamespace(name_);
if (auto result = SetUpPidNamespace(); !result) {
LOG(FATAL) << "Service '" << name_
<< "' could not set up PID namespace: " << result.error();
}
}
for (const auto& [key, value] : environment_vars_) {

View File

@ -124,6 +124,9 @@ class Service {
using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
class OptionParserMap;
Result<Success> SetUpMountNamespace() const;
Result<Success> SetUpPidNamespace() const;
Result<Success> EnterNamespaces() const;
void NotifyStateChange(const std::string& new_state) const;
void StopOrReset(int how);
void ZapStdio() const;
@ -136,6 +139,7 @@ class Service {
Result<Success> ParseConsole(const std::vector<std::string>& args);
Result<Success> ParseCritical(const std::vector<std::string>& args);
Result<Success> ParseDisabled(const std::vector<std::string>& args);
Result<Success> ParseEnterNamespace(const std::vector<std::string>& args);
Result<Success> ParseGroup(const std::vector<std::string>& args);
Result<Success> ParsePriority(const std::vector<std::string>& args);
Result<Success> ParseInterface(const std::vector<std::string>& args);
@ -179,6 +183,8 @@ class Service {
std::vector<gid_t> supp_gids_;
CapSet capabilities_;
unsigned namespace_flags_;
// Pair of namespace type, path to namespace.
std::vector<std::pair<int, std::string>> namespaces_to_enter_;
std::string seclabel_;

View File

@ -261,6 +261,7 @@ cc_test {
"tests/files/offline/jit_debug_arm/*",
"tests/files/offline/jit_debug_x86/*",
"tests/files/offline/gnu_debugdata_arm/*",
"tests/files/offline/offset_arm/*",
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],

View File

@ -160,14 +160,14 @@ uint64_t Elf::GetLastErrorAddress() {
}
// The relative pc is always relative to the start of the map from which it comes.
bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
Memory* process_memory, bool* finished) {
bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
bool* finished) {
if (!valid_) {
return false;
}
// The relative pc expectd by StepIfSignalHandler is relative to the start of the elf.
if (regs->StepIfSignalHandler(rel_pc + elf_offset, this, process_memory)) {
if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) {
*finished = false;
return true;
}

View File

@ -210,8 +210,7 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
in_device_map = true;
} else {
bool finished;
stepped = elf->Step(rel_pc, step_pc, map_info->elf_offset, regs_, process_memory_.get(),
&finished);
stepped = elf->Step(rel_pc, step_pc, regs_, process_memory_.get(), &finished);
elf->GetLastError(&last_error_);
if (stepped && finished) {
break;

View File

@ -65,8 +65,8 @@ class Elf {
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
Memory* process_memory, bool* finished);
bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
bool* finished);
ElfInterface* CreateInterfaceFromMemory(Memory* memory);

View File

@ -133,7 +133,7 @@ TEST_F(ElfTest, elf_invalid) {
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
bool finished;
ASSERT_FALSE(elf.Step(0, 0, 0, nullptr, nullptr, &finished));
ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
}
TEST_F(ElfTest, elf32_invalid_machine) {
@ -330,7 +330,7 @@ TEST_F(ElfTest, step_in_signal_map) {
elf.FakeSetValid(true);
elf.FakeSetLoadBias(0);
bool finished;
ASSERT_TRUE(elf.Step(0x1000, 0x1000, 0x2000, &regs, &process_memory, &finished));
ASSERT_TRUE(elf.Step(0x3000, 0x1000, &regs, &process_memory, &finished));
EXPECT_FALSE(finished);
EXPECT_EQ(15U, regs.pc());
EXPECT_EQ(13U, regs.sp());
@ -370,7 +370,7 @@ TEST_F(ElfTest, step_in_interface) {
EXPECT_CALL(*interface, Step(0x1000, 0, &regs, &process_memory, &finished))
.WillOnce(::testing::Return(true));
ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, &regs, &process_memory, &finished));
ASSERT_TRUE(elf.Step(0x1004, 0x1000, &regs, &process_memory, &finished));
}
TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
@ -388,7 +388,7 @@ TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
EXPECT_CALL(*interface, Step(0x7300, 0x4000, &regs, &process_memory, &finished))
.WillOnce(::testing::Return(true));
ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, &regs, &process_memory, &finished));
ASSERT_TRUE(elf.Step(0x7304, 0x7300, &regs, &process_memory, &finished));
}
TEST_F(ElfTest, get_global_invalid_elf) {

View File

@ -45,6 +45,12 @@
namespace unwindstack {
static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
MemoryOffline* memory = new MemoryOffline;
ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
parts->Add(memory);
}
class UnwindOfflineTest : public ::testing::Test {
protected:
void TearDown() override {
@ -63,9 +69,24 @@ class UnwindOfflineTest : public ::testing::Test {
maps_.reset(new BufferMaps(data.c_str()));
ASSERT_TRUE(maps_->Parse());
std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
process_memory_.reset(stack_memory.release());
std::string stack_name(dir_ + "stack.data");
struct stat st;
if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
process_memory_.reset(stack_memory.release());
} else {
std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
for (size_t i = 0;; i++) {
stack_name = dir_ + "stack" + std::to_string(i) + ".data";
if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
ASSERT_TRUE(i != 0) << "No stack data files found.";
break;
}
AddMemory(stack_name, stack_memory.get());
}
process_memory_.reset(stack_memory.release());
}
switch (arch) {
case ARCH_ARM: {
@ -179,7 +200,7 @@ static std::string DumpFrames(Unwinder& unwinder) {
}
TEST_F(UnwindOfflineTest, pc_straddle_arm) {
Init("straddle_arm/", ARCH_ARM);
ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@ -203,7 +224,7 @@ TEST_F(UnwindOfflineTest, pc_straddle_arm) {
}
TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
Init("gnu_debugdata_arm/", ARCH_ARM);
ASSERT_NO_FATAL_FAILURE(Init("gnu_debugdata_arm/", ARCH_ARM));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@ -223,7 +244,7 @@ TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
}
TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
Init("straddle_arm64/", ARCH_ARM64);
ASSERT_NO_FATAL_FAILURE(Init("straddle_arm64/", ARCH_ARM64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@ -254,14 +275,8 @@ TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
EXPECT_EQ(0x7fe0d84110U, unwinder.frames()[5].sp);
}
static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
MemoryOffline* memory = new MemoryOffline;
ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
parts->Add(memory);
}
TEST_F(UnwindOfflineTest, jit_debug_x86) {
Init("jit_debug_x86/", ARCH_X86);
ASSERT_NO_FATAL_FAILURE(Init("jit_debug_x86/", ARCH_X86));
MemoryOfflineParts* memory = new MemoryOfflineParts;
AddMemory(dir_ + "descriptor.data", memory);
@ -554,7 +569,7 @@ TEST_F(UnwindOfflineTest, jit_debug_x86) {
}
TEST_F(UnwindOfflineTest, jit_debug_arm) {
Init("jit_debug_arm/", ARCH_ARM);
ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
MemoryOfflineParts* memory = new MemoryOfflineParts;
AddMemory(dir_ + "descriptor.data", memory);
@ -872,7 +887,7 @@ TEST_F(UnwindOfflineTest, jit_debug_arm) {
// fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
// No .gnu_debugdata section in the elf file, so no symbols.
TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64);
ASSERT_NO_FATAL_FAILURE(Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@ -901,7 +916,7 @@ TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
// The elf has bad eh_frame unwind information for the pcs. If eh_frame
// is used first, the unwind will not match the expected output.
TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
Init("debug_frame_first_x86/", ARCH_X86);
ASSERT_NO_FATAL_FAILURE(Init("debug_frame_first_x86/", ARCH_X86));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@ -929,7 +944,7 @@ TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
// Make sure that a pc that is at the beginning of an fde unwinds correctly.
TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64);
ASSERT_NO_FATAL_FAILURE(Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@ -956,7 +971,7 @@ TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
}
TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
Init("art_quick_osr_stub_arm/", ARCH_ARM);
ASSERT_NO_FATAL_FAILURE(Init("art_quick_osr_stub_arm/", ARCH_ARM));
MemoryOfflineParts* memory = new MemoryOfflineParts;
AddMemory(dir_ + "descriptor.data", memory);
@ -1070,4 +1085,78 @@ TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
EXPECT_EQ(0xcd4ff960U, unwinder.frames()[24].sp);
}
TEST_F(UnwindOfflineTest, offset_arm) {
ASSERT_NO_FATAL_FAILURE(Init("offset_arm/", ARCH_ARM));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 0032bfa0 (offset 0x42000) libunwindstack_test (SignalInnerFunction+40)\n"
" #01 pc 0032bfeb (offset 0x42000) libunwindstack_test (SignalMiddleFunction+2)\n"
" #02 pc 0032bff3 (offset 0x42000) libunwindstack_test (SignalOuterFunction+2)\n"
" #03 pc 0032fed3 (offset 0x42000) libunwindstack_test "
"(_ZN11unwindstackL19SignalCallerHandlerEiP7siginfoPv+26)\n"
" #04 pc 00026528 (offset 0x25000) libc.so\n"
" #05 pc 00000000 <unknown>\n"
" #06 pc 0032c2d9 (offset 0x42000) libunwindstack_test (InnerFunction+736)\n"
" #07 pc 0032cc4f (offset 0x42000) libunwindstack_test (MiddleFunction+42)\n"
" #08 pc 0032cc81 (offset 0x42000) libunwindstack_test (OuterFunction+42)\n"
" #09 pc 0032e547 (offset 0x42000) libunwindstack_test "
"(_ZN11unwindstackL19RemoteThroughSignalEij+270)\n"
" #10 pc 0032ed99 (offset 0x42000) libunwindstack_test "
"(_ZN11unwindstack55UnwindTest_remote_through_signal_with_invalid_func_Test8TestBodyEv+16)\n"
" #11 pc 00354453 (offset 0x42000) libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
" #12 pc 00354de7 (offset 0x42000) libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
" #13 pc 00355105 (offset 0x42000) libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
" #14 pc 0035a215 (offset 0x42000) libunwindstack_test "
"(_ZN7testing8internal12UnitTestImpl11RunAllTestsEv+664)\n"
" #15 pc 00359f4f (offset 0x42000) libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
" #16 pc 0034d3db (offset 0x42000) libunwindstack_test (main+38)\n"
" #17 pc 00092c0d (offset 0x25000) libc.so (__libc_init+48)\n"
" #18 pc 0004202f (offset 0x42000) libunwindstack_test (_start_main+38)\n",
frame_info);
EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
EXPECT_EQ(0xf43d2cccU, unwinder.frames()[0].sp);
EXPECT_EQ(0x2e55febU, unwinder.frames()[1].pc);
EXPECT_EQ(0xf43d2ce0U, unwinder.frames()[1].sp);
EXPECT_EQ(0x2e55ff3U, unwinder.frames()[2].pc);
EXPECT_EQ(0xf43d2ce8U, unwinder.frames()[2].sp);
EXPECT_EQ(0x2e59ed3U, unwinder.frames()[3].pc);
EXPECT_EQ(0xf43d2cf0U, unwinder.frames()[3].sp);
EXPECT_EQ(0xf4136528U, unwinder.frames()[4].pc);
EXPECT_EQ(0xf43d2d10U, unwinder.frames()[4].sp);
EXPECT_EQ(0U, unwinder.frames()[5].pc);
EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[5].sp);
EXPECT_EQ(0x2e562d9U, unwinder.frames()[6].pc);
EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[6].sp);
EXPECT_EQ(0x2e56c4fU, unwinder.frames()[7].pc);
EXPECT_EQ(0xffcc1060U, unwinder.frames()[7].sp);
EXPECT_EQ(0x2e56c81U, unwinder.frames()[8].pc);
EXPECT_EQ(0xffcc1078U, unwinder.frames()[8].sp);
EXPECT_EQ(0x2e58547U, unwinder.frames()[9].pc);
EXPECT_EQ(0xffcc1090U, unwinder.frames()[9].sp);
EXPECT_EQ(0x2e58d99U, unwinder.frames()[10].pc);
EXPECT_EQ(0xffcc1438U, unwinder.frames()[10].sp);
EXPECT_EQ(0x2e7e453U, unwinder.frames()[11].pc);
EXPECT_EQ(0xffcc1448U, unwinder.frames()[11].sp);
EXPECT_EQ(0x2e7ede7U, unwinder.frames()[12].pc);
EXPECT_EQ(0xffcc1458U, unwinder.frames()[12].sp);
EXPECT_EQ(0x2e7f105U, unwinder.frames()[13].pc);
EXPECT_EQ(0xffcc1490U, unwinder.frames()[13].sp);
EXPECT_EQ(0x2e84215U, unwinder.frames()[14].pc);
EXPECT_EQ(0xffcc14c0U, unwinder.frames()[14].sp);
EXPECT_EQ(0x2e83f4fU, unwinder.frames()[15].pc);
EXPECT_EQ(0xffcc1510U, unwinder.frames()[15].sp);
EXPECT_EQ(0x2e773dbU, unwinder.frames()[16].pc);
EXPECT_EQ(0xffcc1528U, unwinder.frames()[16].sp);
EXPECT_EQ(0xf41a2c0dU, unwinder.frames()[17].pc);
EXPECT_EQ(0xffcc1540U, unwinder.frames()[17].sp);
EXPECT_EQ(0x2b6c02fU, unwinder.frames()[18].pc);
EXPECT_EQ(0xffcc1558U, unwinder.frames()[18].sp);
}
} // namespace unwindstack

Binary file not shown.

View File

@ -0,0 +1,2 @@
2b6c000-2e92000 r-xp 42000 00:00 0 libunwindstack_test
f4135000-f41a9000 r-xp 25000 00:00 0 libc.so

View File

@ -0,0 +1,16 @@
r0: 5
r1: 5
r2: 4
r3: 1
r4: 73804b6b
r5: f3c9c000
r6: 2ea09ac
r7: 10624dd3
r8: f41b5d8c
r9: f3c9c000
r10: 6f17
r11: f3c94048
ip: 2ea0807
sp: f43d2ccc
lr: 2e55fef
pc: 2e55fa0

View File

@ -30,6 +30,7 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <unwindstack/Elf.h>
@ -78,30 +79,44 @@ bool SaveRegs(unwindstack::Regs* regs) {
return true;
}
bool SaveStack(pid_t pid, uint64_t sp_start, uint64_t sp_end) {
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("stack.data", "w+"), &fclose);
if (fp == nullptr) {
printf("Failed to create stack.data.\n");
return false;
}
bool SaveStack(pid_t pid, const std::vector<std::pair<uint64_t, uint64_t>>& stacks) {
for (size_t i = 0; i < stacks.size(); i++) {
std::string file_name;
if (stacks.size() != 1) {
file_name = "stack" + std::to_string(i) + ".data";
} else {
file_name = "stack.data";
}
size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
if (bytes != sizeof(sp_start)) {
perror("Failed to write all data.");
return false;
}
// Do this first, so if it fails, we don't create the file.
uint64_t sp_start = stacks[i].first;
uint64_t sp_end = stacks[i].second;
std::vector<uint8_t> buffer(sp_end - sp_start);
auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
printf("Unable to read stack data.\n");
return false;
}
std::vector<uint8_t> buffer(sp_end - sp_start);
auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
printf("Unable to read stack data.\n");
return false;
}
printf("Saving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end);
bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
if (bytes != buffer.size()) {
printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
return 1;
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file_name.c_str(), "w+"), &fclose);
if (fp == nullptr) {
printf("Failed to create stack.data.\n");
return false;
}
size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
if (bytes != sizeof(sp_start)) {
perror("Failed to write all data.");
return false;
}
bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
if (bytes != buffer.size()) {
printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
return false;
}
}
return true;
@ -110,17 +125,11 @@ bool SaveStack(pid_t pid, uint64_t sp_start, uint64_t sp_end) {
bool CreateElfFromMemory(std::shared_ptr<unwindstack::Memory>& memory, map_info_t* info) {
std::string cur_name;
if (info->name.empty()) {
cur_name = android::base::StringPrintf("anonymous:%" PRIx64, info->start);
cur_name = android::base::StringPrintf("anonymous_%" PRIx64, info->start);
} else {
cur_name = basename(info->name.c_str());
cur_name = android::base::StringPrintf("%s:%" PRIx64, basename(info->name.c_str()), info->start);
cur_name = android::base::StringPrintf("%s_%" PRIx64, basename(info->name.c_str()), info->start);
}
std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
if (output == nullptr) {
printf("Cannot create %s\n", cur_name.c_str());
return false;
}
std::vector<uint8_t> buffer(info->end - info->start);
// If this is a mapped in file, it might not be possible to read the entire
// map, so read all that is readable.
@ -129,6 +138,13 @@ bool CreateElfFromMemory(std::shared_ptr<unwindstack::Memory>& memory, map_info_
printf("Cannot read data from address %" PRIx64 " length %zu\n", info->start, buffer.size());
return false;
}
std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
if (output == nullptr) {
printf("Cannot create %s\n", cur_name.c_str());
return false;
}
size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
if (bytes_written != bytes) {
printf("Failed to write all data to file: bytes read %zu, written %zu\n", bytes, bytes_written);
@ -198,9 +214,21 @@ int SaveData(pid_t pid) {
unwinder.Unwind();
std::unordered_map<uint64_t, map_info_t> maps_by_start;
uint64_t last_sp;
std::vector<std::pair<uint64_t, uint64_t>> stacks;
uint64_t sp_map_start = 0;
unwindstack::MapInfo* map_info = maps.Find(sp);
if (map_info != nullptr) {
stacks.emplace_back(std::make_pair(sp, map_info->end));
sp_map_start = map_info->start;
}
for (auto frame : unwinder.frames()) {
last_sp = frame.sp;
map_info = maps.Find(frame.sp);
if (map_info != nullptr && sp_map_start != map_info->start) {
stacks.emplace_back(std::make_pair(frame.sp, map_info->end));
sp_map_start = map_info->start;
}
if (maps_by_start.count(frame.map_start) == 0) {
auto info = &maps_by_start[frame.map_start];
info->start = frame.map_start;
@ -211,7 +239,12 @@ int SaveData(pid_t pid) {
// Try to create the elf from memory, this will handle cases where
// the data only exists in memory such as vdso data on x86.
if (!CreateElfFromMemory(process_memory, info)) {
return 1;
printf("Ignoring map ");
if (!info->name.empty()) {
printf("%s\n", info->name.c_str());
} else {
printf("anonymous:%" PRIx64 "\n", info->start);
}
}
}
}
@ -221,7 +254,7 @@ int SaveData(pid_t pid) {
printf("%s\n", unwinder.FormatFrame(i).c_str());
}
if (!SaveStack(pid, sp, last_sp)) {
if (!SaveStack(pid, stacks)) {
return 1;
}

View File

@ -429,6 +429,7 @@ on post-fs-data
mkdir /data/misc/radio 0770 system radio
mkdir /data/misc/sms 0770 system radio
mkdir /data/misc/carrierid 0770 system radio
mkdir /data/misc/apns 0770 system radio
mkdir /data/misc/zoneinfo 0775 system system
mkdir /data/misc/network_watchlist 0774 system system
mkdir /data/misc/textclassifier 0771 system system