Automated rollback of commit a850aa44d2.

PiperOrigin-RevId: 380897565
Change-Id: Iacc50697a5ff25b79272a1549291bbf32152d3f6
This commit is contained in:
Sandboxed API Team 2021-06-22 14:49:54 -07:00 committed by Copybara-Service
parent 0ec4f07f96
commit 4a38f59728
7 changed files with 167 additions and 199 deletions

View File

@ -68,7 +68,6 @@
#include "sandboxed_api/sandbox2/util.h" #include "sandboxed_api/sandbox2/util.h"
#include "sandboxed_api/util/file_helpers.h" #include "sandboxed_api/util/file_helpers.h"
#include "sandboxed_api/util/raw_logging.h" #include "sandboxed_api/util/raw_logging.h"
#include "sandboxed_api/util/status_macros.h"
#include "sandboxed_api/util/temp_file.h" #include "sandboxed_api/util/temp_file.h"
using std::string; using std::string;
@ -374,15 +373,8 @@ void Monitor::SetAdditionalResultInfo(std::unique_ptr<Regs> regs) {
} }
auto* ns = policy_->GetNamespace(); auto* ns = policy_->GetNamespace();
const Mounts empty_mounts; const Mounts empty_mounts;
absl::StatusOr<std::vector<std::string>> stack_trace = result_.set_stack_trace(
GetStackTrace(result_.GetRegs(), ns ? ns->mounts() : empty_mounts); GetStackTrace(result_.GetRegs(), ns ? ns->mounts() : empty_mounts));
if (!stack_trace.ok()) {
LOG(ERROR) << "Could not obtain stack trace: " << stack_trace.status();
return;
}
result_.set_stack_trace(*stack_trace);
LOG(INFO) << "Stack trace: ["; LOG(INFO) << "Stack trace: [";
for (const auto& frame : CompactStackTrace(result_.stack_trace())) { for (const auto& frame : CompactStackTrace(result_.stack_trace())) {
@ -944,18 +936,13 @@ void Monitor::StateProcessStopped(pid_t pid, int status) {
if (ABSL_PREDICT_FALSE(pid == pid_ && should_dump_stack_ && if (ABSL_PREDICT_FALSE(pid == pid_ && should_dump_stack_ &&
executor_->libunwind_sbox_for_pid_ == 0 && executor_->libunwind_sbox_for_pid_ == 0 &&
policy_->GetNamespace())) { policy_->GetNamespace())) {
auto stack_trace = [this,
pid]() -> absl::StatusOr<std::vector<std::string>> {
Regs regs(pid); Regs regs(pid);
SAPI_RETURN_IF_ERROR(regs.Fetch()); if (auto status = regs.Fetch(); !status.ok()) {
return GetStackTrace(&regs, policy_->GetNamespace()->mounts()); LOG(WARNING) << "FAILED TO GET SANDBOX STACK : " << status;
}();
if (!stack_trace.ok()) {
LOG(WARNING) << "FAILED TO GET SANDBOX STACK : " << stack_trace.status();
} else if (SAPI_VLOG_IS_ON(0)) { } else if (SAPI_VLOG_IS_ON(0)) {
VLOG(0) << "SANDBOX STACK: PID: " << pid << ", ["; VLOG(0) << "SANDBOX STACK: PID: " << pid << ", [";
for (const auto& frame : *stack_trace) { for (const auto& frame :
GetStackTrace(&regs, policy_->GetNamespace()->mounts())) {
VLOG(0) << " " << frame; VLOG(0) << " " << frame;
} }
VLOG(0) << "]"; VLOG(0) << "]";

View File

@ -24,10 +24,8 @@
#include <vector> #include <vector>
#include <glog/logging.h> #include <glog/logging.h>
#include "absl/cleanup/cleanup.h"
#include "sandboxed_api/util/flag.h" #include "sandboxed_api/util/flag.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/numbers.h" #include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/strip.h" #include "absl/strings/strip.h"
@ -47,7 +45,6 @@
#include "sandboxed_api/sandbox2/util/bpf_helper.h" #include "sandboxed_api/sandbox2/util/bpf_helper.h"
#include "sandboxed_api/util/fileops.h" #include "sandboxed_api/util/fileops.h"
#include "sandboxed_api/util/path.h" #include "sandboxed_api/util/path.h"
#include "sandboxed_api/util/status_macros.h"
ABSL_FLAG(bool, sandbox_disable_all_stack_traces, false, ABSL_FLAG(bool, sandbox_disable_all_stack_traces, false,
"Completely disable stack trace collection for sandboxees"); "Completely disable stack trace collection for sandboxees");
@ -56,34 +53,27 @@ ABSL_FLAG(bool, sandbox_libunwind_crash_handler, true,
"Sandbox libunwind when handling violations (preferred)"); "Sandbox libunwind when handling violations (preferred)");
namespace sandbox2 { namespace sandbox2 {
namespace {
namespace file = ::sapi::file; namespace file = ::sapi::file;
namespace file_util = ::sapi::file_util; namespace file_util = ::sapi::file_util;
// Similar to GetStackTrace() but without using the sandbox to isolate
// libunwind.
absl::StatusOr<std::vector<std::string>> UnsafeGetStackTrace(pid_t pid) {
LOG(WARNING) << "Using non-sandboxed libunwind";
return RunLibUnwindAndSymbolizer(pid, kDefaultMaxFrames);
}
} // namespace
class StackTracePeer { class StackTracePeer {
public: public:
static absl::StatusOr<std::unique_ptr<Policy>> GetPolicy( static std::unique_ptr<Policy> GetPolicy(pid_t target_pid,
pid_t target_pid, const std::string& maps_file, const std::string& maps_file,
const std::string& app_path, const std::string& exe_path, const std::string& app_path,
const std::string& exe_path,
const Mounts& mounts); const Mounts& mounts);
static absl::StatusOr<UnwindResult> LaunchLibunwindSandbox( static bool LaunchLibunwindSandbox(const Regs* regs, const Mounts& mounts,
const Regs* regs, const Mounts& mounts); UnwindResult* result);
}; };
absl::StatusOr<std::unique_ptr<Policy>> StackTracePeer::GetPolicy( std::unique_ptr<Policy> StackTracePeer::GetPolicy(pid_t target_pid,
pid_t target_pid, const std::string& maps_file, const std::string& app_path, const std::string& maps_file,
const std::string& exe_path, const Mounts& mounts) { const std::string& app_path,
const std::string& exe_path,
const Mounts& mounts) {
PolicyBuilder builder; PolicyBuilder builder;
builder builder
// Use the mounttree of the original executable as starting point. // Use the mounttree of the original executable as starting point.
@ -157,18 +147,23 @@ absl::StatusOr<std::unique_ptr<Policy>> StackTracePeer::GetPolicy(
} }
} }
SAPI_ASSIGN_OR_RETURN(std::unique_ptr<Policy> policy, builder.TryBuild()); auto policy = builder.TryBuild();
if (!policy.ok()) {
LOG(ERROR) << "Creating stack unwinder sandbox policy failed";
return nullptr;
}
auto keep_capabilities = absl::make_unique<std::vector<int>>(); auto keep_capabilities = absl::make_unique<std::vector<int>>();
keep_capabilities->push_back(CAP_SYS_PTRACE); keep_capabilities->push_back(CAP_SYS_PTRACE);
policy->AllowUnsafeKeepCapabilities(std::move(keep_capabilities)); (*policy)->AllowUnsafeKeepCapabilities(std::move(keep_capabilities));
// Use no special namespace flags when cloning. We will join an existing // Use no special namespace flags when cloning. We will join an existing
// user namespace and will unshare() afterwards (See forkserver.cc). // user namespace and will unshare() afterwards (See forkserver.cc).
policy->GetNamespace()->clone_flags_ = 0; (*policy)->GetNamespace()->clone_flags_ = 0;
return std::move(policy); return std::move(*policy);
} }
absl::StatusOr<UnwindResult> StackTracePeer::LaunchLibunwindSandbox( bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
const Regs* regs, const Mounts& mounts) { const Mounts& mounts,
sandbox2::UnwindResult* result) {
const pid_t pid = regs->pid(); const pid_t pid = regs->pid();
// Tell executor to use this special internal mode. // Tell executor to use this special internal mode.
@ -188,8 +183,8 @@ absl::StatusOr<UnwindResult> StackTracePeer::LaunchLibunwindSandbox(
char unwind_temp_directory_template[] = "/tmp/.sandbox2_unwind_XXXXXX"; char unwind_temp_directory_template[] = "/tmp/.sandbox2_unwind_XXXXXX";
char* unwind_temp_directory = mkdtemp(unwind_temp_directory_template); char* unwind_temp_directory = mkdtemp(unwind_temp_directory_template);
if (!unwind_temp_directory) { if (!unwind_temp_directory) {
return absl::InternalError( LOG(WARNING) << "Could not create temporary directory for unwinding";
"Could not create temporary directory for unwinding"); return false;
} }
struct UnwindTempDirectoryCleanup { struct UnwindTempDirectoryCleanup {
~UnwindTempDirectoryCleanup() { ~UnwindTempDirectoryCleanup() {
@ -205,7 +200,8 @@ absl::StatusOr<UnwindResult> StackTracePeer::LaunchLibunwindSandbox(
if (!file_util::fileops::CopyFile( if (!file_util::fileops::CopyFile(
file::JoinPath("/proc", absl::StrCat(pid), "maps"), file::JoinPath("/proc", absl::StrCat(pid), "maps"),
unwind_temp_maps_path, 0400)) { unwind_temp_maps_path, 0400)) {
return absl::InternalError("Could not copy maps file"); LOG(WARNING) << "Could not copy maps file";
return false;
} }
// Get path to the binary. // Get path to the binary.
@ -215,7 +211,8 @@ absl::StatusOr<UnwindResult> StackTracePeer::LaunchLibunwindSandbox(
std::string app_path; std::string app_path;
std::string proc_pid_exe = file::JoinPath("/proc", absl::StrCat(pid), "exe"); std::string proc_pid_exe = file::JoinPath("/proc", absl::StrCat(pid), "exe");
if (!file_util::fileops::ReadLinkAbsolute(proc_pid_exe, &app_path)) { if (!file_util::fileops::ReadLinkAbsolute(proc_pid_exe, &app_path)) {
return absl::InternalError("Could not obtain absolute path to the binary"); LOG(WARNING) << "Could not obtain absolute path to the binary";
return false;
} }
// The exe_path will have a mountable path of the application, even if it was // The exe_path will have a mountable path of the application, even if it was
@ -230,7 +227,8 @@ absl::StatusOr<UnwindResult> StackTracePeer::LaunchLibunwindSandbox(
// Create a copy of /proc/pid/exe, mount that one. // Create a copy of /proc/pid/exe, mount that one.
exe_path = file::JoinPath(unwind_temp_directory, "exe"); exe_path = file::JoinPath(unwind_temp_directory, "exe");
if (!file_util::fileops::CopyFile(proc_pid_exe, exe_path, 0700)) { if (!file_util::fileops::CopyFile(proc_pid_exe, exe_path, 0700)) {
return absl::InternalError("Could not copy /proc/pid/exe"); LOG(WARNING) << "Could not copy /proc/pid/exe";
return false;
} }
} }
@ -238,9 +236,11 @@ absl::StatusOr<UnwindResult> StackTracePeer::LaunchLibunwindSandbox(
// Add mappings for the binary (as they might not have been added due to the // Add mappings for the binary (as they might not have been added due to the
// forkserver). // forkserver).
SAPI_ASSIGN_OR_RETURN(std::unique_ptr<Policy> policy, auto policy = StackTracePeer::GetPolicy(pid, unwind_temp_maps_path, app_path,
StackTracePeer::GetPolicy(pid, unwind_temp_maps_path, exe_path, mounts);
app_path, exe_path, mounts)); if (!policy) {
return false;
}
Sandbox2 sandbox(std::move(executor), std::move(policy)); Sandbox2 sandbox(std::move(executor), std::move(policy));
VLOG(1) << "Running libunwind sandbox"; VLOG(1) << "Running libunwind sandbox";
@ -253,53 +253,37 @@ absl::StatusOr<UnwindResult> StackTracePeer::LaunchLibunwindSandbox(
sizeof(regs->user_regs_)); sizeof(regs->user_regs_));
msg.set_default_max_frames(kDefaultMaxFrames); msg.set_default_max_frames(kDefaultMaxFrames);
absl::Cleanup kill_sandbox = [&sandbox]() { bool success = true;
sandbox.Kill();
sandbox.AwaitResult().IgnoreResult();
};
if (!comms->SendProtoBuf(msg)) { if (!comms->SendProtoBuf(msg)) {
return absl::InternalError("Sending libunwind setup message failed"); LOG(ERROR) << "Sending libunwind setup message failed";
} success = false;
absl::Status status;
if (!comms->RecvStatus(&status)) {
return absl::InternalError(
"Receiving status from libunwind sandbox failed");
}
if (!status.ok()) {
return status;
}
UnwindResult result;
if (!comms->RecvProtoBuf(&result)) {
return absl::InternalError("Receiving libunwind result failed");
} }
std::move(kill_sandbox).Cancel(); if (success && !comms->RecvProtoBuf(result)) {
LOG(ERROR) << "Receiving libunwind result failed";
success = false;
}
if (!success) {
sandbox.Kill();
}
auto sandbox_result = sandbox.AwaitResult(); auto sandbox_result = sandbox.AwaitResult();
LOG(INFO) << "Libunwind execution status: " << sandbox_result.ToString(); LOG(INFO) << "Libunwind execution status: " << sandbox_result.ToString();
if (sandbox_result.final_status() != Result::OK) { return success && sandbox_result.final_status() == Result::OK;
return absl::InternalError(
absl::StrCat("libunwind sandbox did not finish properly: ",
sandbox_result.ToString()));
} }
return result; std::vector<std::string> GetStackTrace(const Regs* regs, const Mounts& mounts) {
}
absl::StatusOr<std::vector<std::string>> GetStackTrace(const Regs* regs,
const Mounts& mounts) {
if constexpr (sapi::host_cpu::IsArm64()) { if constexpr (sapi::host_cpu::IsArm64()) {
return absl::UnavailableError("Stack traces unavailable on Aarch64"); return {"[Stack traces unavailable]"};
} }
if (absl::GetFlag(FLAGS_sandbox_disable_all_stack_traces)) { if (absl::GetFlag(FLAGS_sandbox_disable_all_stack_traces)) {
return absl::UnavailableError("Stacktraces disabled"); return {"[Stacktraces disabled]"};
} }
if (!regs) { if (!regs) {
return absl::InvalidArgumentError( LOG(WARNING) << "Could not obtain stacktrace, regs == nullptr";
"Could not obtain stacktrace, regs == nullptr"); return {"[ERROR (noregs)]"};
} }
// Show a warning if sandboxed libunwind is requested but we're running in // Show a warning if sandboxed libunwind is requested but we're running in
@ -319,10 +303,17 @@ absl::StatusOr<std::vector<std::string>> GetStackTrace(const Regs* regs,
return UnsafeGetStackTrace(regs->pid()); return UnsafeGetStackTrace(regs->pid());
} }
SAPI_ASSIGN_OR_RETURN(UnwindResult res, UnwindResult res;
StackTracePeer::LaunchLibunwindSandbox(regs, mounts)); if (!StackTracePeer::LaunchLibunwindSandbox(regs, mounts, &res)) {
return std::vector<std::string>(res.stacktrace().begin(), return {};
res.stacktrace().end()); }
return {res.stacktrace().begin(), res.stacktrace().end()};
}
std::vector<std::string> UnsafeGetStackTrace(pid_t pid) {
LOG(WARNING) << "Using non-sandboxed libunwind";
std::vector<uintptr_t> ips;
return RunLibUnwindAndSymbolizer(pid, &ips, kDefaultMaxFrames);
} }
std::vector<std::string> CompactStackTrace( std::vector<std::string> CompactStackTrace(

View File

@ -19,11 +19,15 @@
#ifndef SANDBOXED_API_SANDBOX2_STACK_TRACE_H_ #ifndef SANDBOXED_API_SANDBOX2_STACK_TRACE_H_
#define SANDBOXED_API_SANDBOX2_STACK_TRACE_H_ #define SANDBOXED_API_SANDBOX2_STACK_TRACE_H_
#include <sys/types.h>
#include <cstddef>
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include "absl/status/statusor.h"
#include "sandboxed_api/sandbox2/mounts.h" #include "sandboxed_api/sandbox2/mounts.h"
#include "sandboxed_api/sandbox2/policy.h"
#include "sandboxed_api/sandbox2/regs.h" #include "sandboxed_api/sandbox2/regs.h"
namespace sandbox2 { namespace sandbox2 {
@ -32,8 +36,11 @@ namespace sandbox2 {
constexpr size_t kDefaultMaxFrames = 200; constexpr size_t kDefaultMaxFrames = 200;
// Returns the stack-trace of the PID=pid, one line per frame. // Returns the stack-trace of the PID=pid, one line per frame.
absl::StatusOr<std::vector<std::string>> GetStackTrace(const Regs* regs, std::vector<std::string> GetStackTrace(const Regs* regs, const Mounts& mounts);
const Mounts& mounts);
// Similar to GetStackTrace() but without using the sandbox to isolate
// libunwind.
std::vector<std::string> UnsafeGetStackTrace(pid_t pid);
// Returns a stack trace that collapses duplicate stack frames and annotates // Returns a stack trace that collapses duplicate stack frames and annotates
// them with a repetition count. // them with a repetition count.

View File

@ -59,9 +59,7 @@ cc_library(
"//sandboxed_api/sandbox2/util:minielf", "//sandboxed_api/sandbox2/util:minielf",
"//sandboxed_api/util:file_helpers", "//sandboxed_api/util:file_helpers",
"//sandboxed_api/util:raw_logging", "//sandboxed_api/util:raw_logging",
"//sandboxed_api/util:status",
"//sandboxed_api/util:strerror", "//sandboxed_api/util:strerror",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings", "@com_google_absl//absl/strings",
"@org_gnu_libunwind//:unwind-ptrace-wrapped", "@org_gnu_libunwind//:unwind-ptrace-wrapped",
], ],

View File

@ -30,7 +30,6 @@ add_library(sandbox2_unwind STATIC
) )
add_library(sandbox2::unwind ALIAS sandbox2_unwind) add_library(sandbox2::unwind ALIAS sandbox2_unwind)
target_link_libraries(sandbox2_unwind PRIVATE target_link_libraries(sandbox2_unwind PRIVATE
absl::statusor
absl::strings absl::strings
sandbox2::comms sandbox2::comms
sandbox2::maps_parser sandbox2::maps_parser
@ -41,7 +40,6 @@ target_link_libraries(sandbox2_unwind PRIVATE
sapi::base sapi::base
sapi::file_helpers sapi::file_helpers
sapi::raw_logging sapi::raw_logging
sapi::status
unwind::unwind_ptrace_wrapped unwind::unwind_ptrace_wrapped
) )

View File

@ -24,7 +24,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "absl/status/statusor.h"
#include "absl/strings/match.h" #include "absl/strings/match.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "libunwind-ptrace.h" #include "libunwind-ptrace.h"
@ -35,7 +34,6 @@
#include "sandboxed_api/sandbox2/util/minielf.h" #include "sandboxed_api/sandbox2/util/minielf.h"
#include "sandboxed_api/util/file_helpers.h" #include "sandboxed_api/util/file_helpers.h"
#include "sandboxed_api/util/raw_logging.h" #include "sandboxed_api/util/raw_logging.h"
#include "sandboxed_api/util/status_macros.h"
#include "sandboxed_api/util/strerror.h" #include "sandboxed_api/util/strerror.h"
namespace sandbox2 { namespace sandbox2 {
@ -75,77 +73,29 @@ std::string GetSymbolAt(const std::map<uint64_t, std::string>& addr_to_symbol,
return ""; return "";
} }
absl::StatusOr<std::map<uint64_t, std::string>> LoadSymbolsMap(pid_t pid) { } // namespace
const std::string maps_filename = absl::StrCat("/proc/", pid, "/maps");
std::string maps_content;
SAPI_RETURN_IF_ERROR(sapi::file::GetContents(maps_filename, &maps_content,
sapi::file::Defaults()));
SAPI_ASSIGN_OR_RETURN(std::vector<MapsEntry> maps, std::vector<uintptr_t> GetIPList(pid_t pid, int max_frames) {
ParseProcMaps(maps_content));
// Get symbols for each file entry in the maps entry.
// This is not a very efficient way, so we might want to optimize it.
std::map<uint64_t, std::string> addr_to_symbol;
for (const MapsEntry& entry : maps) {
if (!entry.is_executable ||
entry.inode == 0 || // Only parse file-backed entries
entry.path.empty() ||
absl::EndsWith(entry.path, " (deleted)") // Skip deleted files
) {
continue;
}
// Store details about start + end of this map.
// The maps entries are ordered and thus sorted with increasing adresses.
// This means if there is a symbol @ entry.end, it will be overwritten in
// the next iteration.
addr_to_symbol[entry.start] = absl::StrCat("map:", entry.path);
addr_to_symbol[entry.end] = "";
absl::StatusOr<ElfFile> elf =
ElfFile::ParseFromFile(entry.path, ElfFile::kLoadSymbols);
if (!elf.ok()) {
SAPI_RAW_LOG(WARNING, "Could not load symbols for %s: %s",
entry.path.c_str(),
std::string(elf.status().message()).c_str());
continue;
}
for (const ElfFile::Symbol& symbol : elf->symbols()) {
if (elf->position_independent()) {
if (symbol.address < entry.end - entry.start) {
addr_to_symbol[symbol.address + entry.start] = symbol.name;
}
} else {
if (symbol.address >= entry.start && symbol.address < entry.end) {
addr_to_symbol[symbol.address] = symbol.name;
}
}
}
}
return addr_to_symbol;
}
absl::StatusOr<std::vector<uintptr_t>> RunLibUnwind(pid_t pid, int max_frames) {
unw_cursor_t cursor; unw_cursor_t cursor;
static unw_addr_space_t as = static unw_addr_space_t as =
unw_create_addr_space(&_UPT_accessors, 0 /* byte order */); unw_create_addr_space(&_UPT_accessors, 0 /* byte order */);
if (as == nullptr) { if (as == nullptr) {
return absl::InternalError("unw_create_addr_space() failed"); SAPI_RAW_LOG(WARNING, "unw_create_addr_space() failed");
return {};
} }
std::unique_ptr<struct UPT_info, void (*)(void*)> ui( std::unique_ptr<struct UPT_info, void (*)(void*)> ui(
reinterpret_cast<struct UPT_info*>(_UPT_create(pid)), _UPT_destroy); reinterpret_cast<struct UPT_info*>(_UPT_create(pid)), _UPT_destroy);
if (ui == nullptr) { if (ui == nullptr) {
return absl::InternalError("_UPT_create() failed"); SAPI_RAW_LOG(WARNING, "_UPT_create() failed");
return {};
} }
int rc = unw_init_remote(&cursor, as, ui.get()); int rc = unw_init_remote(&cursor, as, ui.get());
if (rc < 0) { if (rc < 0) {
// Could be UNW_EINVAL (8), UNW_EUNSPEC (1) or UNW_EBADREG (3). // Could be UNW_EINVAL (8), UNW_EUNSPEC (1) or UNW_EBADREG (3).
return absl::InternalError( SAPI_RAW_LOG(WARNING, "unw_init_remote() failed with error %d", rc);
absl::StrCat("unw_init_remote() failed with error ", rc)); return {};
} }
std::vector<uintptr_t> ips; std::vector<uintptr_t> ips;
@ -170,22 +120,6 @@ absl::StatusOr<std::vector<uintptr_t>> RunLibUnwind(pid_t pid, int max_frames) {
return ips; return ips;
} }
absl::StatusOr<std::vector<std::string>> SymbolizeStacktrace(
pid_t pid, const std::vector<uintptr_t>& ips) {
SAPI_ASSIGN_OR_RETURN(auto addr_to_symbol, LoadSymbolsMap(pid));
std::vector<std::string> stack_trace;
stack_trace.reserve(ips.size());
// Symbolize stacktrace
for (uintptr_t ip : ips) {
const std::string symbol =
GetSymbolAt(addr_to_symbol, static_cast<uint64_t>(ip));
stack_trace.push_back(absl::StrCat(symbol, "(0x", absl::Hex(ip), ")"));
}
return stack_trace;
}
} // namespace
bool RunLibUnwindAndSymbolizer(Comms* comms) { bool RunLibUnwindAndSymbolizer(Comms* comms) {
UnwindSetup setup; UnwindSetup setup;
if (!comms->RecvProtoBuf(&setup)) { if (!comms->RecvProtoBuf(&setup)) {
@ -194,34 +128,87 @@ bool RunLibUnwindAndSymbolizer(Comms* comms) {
EnablePtraceEmulationWithUserRegs(setup.regs()); EnablePtraceEmulationWithUserRegs(setup.regs());
absl::StatusOr<std::vector<uintptr_t>> ips = std::vector<uintptr_t> ips;
RunLibUnwind(setup.pid(), setup.default_max_frames()); std::vector<std::string> stack_trace =
absl::StatusOr<std::vector<std::string>> stack_trace; RunLibUnwindAndSymbolizer(setup.pid(), &ips, setup.default_max_frames());
if (ips.ok()) {
stack_trace = SymbolizeStacktrace(setup.pid(), *ips);
} else {
stack_trace = ips.status();
}
if (!comms->SendStatus(stack_trace.status())) {
return false;
}
if (!stack_trace.ok()) {
return true;
}
UnwindResult msg; UnwindResult msg;
*msg.mutable_stacktrace() = {stack_trace->begin(), stack_trace->end()}; *msg.mutable_stacktrace() = {stack_trace.begin(), stack_trace.end()};
*msg.mutable_ip() = {ips->begin(), ips->end()}; *msg.mutable_ip() = {ips.begin(), ips.end()};
return comms->SendProtoBuf(msg); return comms->SendProtoBuf(msg);
} }
absl::StatusOr<std::vector<std::string>> RunLibUnwindAndSymbolizer( std::vector<std::string> RunLibUnwindAndSymbolizer(pid_t pid,
pid_t pid, int max_frames) { std::vector<uintptr_t>* ips,
SAPI_ASSIGN_OR_RETURN(std::vector<uintptr_t> ips, int max_frames) {
RunLibUnwind(pid, max_frames)); *ips = GetIPList(pid, max_frames); // Uses libunwind
return SymbolizeStacktrace(pid, ips);
const std::string maps_filename = absl::StrCat("/proc/", pid, "/maps");
std::string maps_content;
if (auto status = sapi::file::GetContents(maps_filename, &maps_content,
sapi::file::Defaults());
!status.ok()) {
SAPI_RAW_LOG(ERROR, "%s", status.ToString().c_str());
return {};
}
auto maps = ParseProcMaps(maps_content);
if (!maps.ok()) {
SAPI_RAW_LOG(ERROR, "Could not parse file: %s", maps_filename.c_str());
return {};
}
// Get symbols for each file entry in the maps entry.
// This is not a very efficient way, so we might want to optimize it.
std::map<uint64_t, std::string> addr_to_symbol;
for (const auto& entry : *maps) {
if (!entry.path.empty()) {
// Store details about start + end of this map.
// The maps entries are ordered and thus sorted with increasing adresses.
// This means if there is a symbol @ entry.end, it will be overwritten in
// the next iteration.
addr_to_symbol[entry.start] = absl::StrCat("map:", entry.path);
addr_to_symbol[entry.end] = "";
}
if (!entry.is_executable ||
entry.inode == 0 || // Only parse file-backed entries
entry.path.empty() ||
absl::EndsWith(entry.path, " (deleted)") // Skip deleted files
) {
continue;
}
auto elf = ElfFile::ParseFromFile(entry.path, ElfFile::kLoadSymbols);
if (!elf.ok()) {
SAPI_RAW_LOG(WARNING, "Could not load symbols for %s: %s",
entry.path.c_str(),
std::string(elf.status().message()).c_str());
continue;
}
for (const auto& symbol : elf->symbols()) {
if (elf->position_independent()) {
if (symbol.address < entry.end - entry.start) {
addr_to_symbol[symbol.address + entry.start] = symbol.name;
}
} else {
if (symbol.address >= entry.start && symbol.address < entry.end) {
addr_to_symbol[symbol.address] = symbol.name;
}
}
}
}
std::vector<std::string> stack_trace;
stack_trace.reserve(ips->size());
// Symbolize stacktrace
for (const auto& ip : *ips) {
const std::string symbol =
GetSymbolAt(addr_to_symbol, static_cast<uint64_t>(ip));
stack_trace.push_back(absl::StrCat(symbol, "(0x", absl::Hex(ip), ")"));
}
return stack_trace;
} }
} // namespace sandbox2 } // namespace sandbox2

View File

@ -21,7 +21,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "absl/status/statusor.h"
#include "sandboxed_api/sandbox2/comms.h" #include "sandboxed_api/sandbox2/comms.h"
namespace sandbox2 { namespace sandbox2 {
@ -29,8 +28,9 @@ namespace sandbox2 {
// Runs libunwind and the symbolizer and sends the results via comms. // Runs libunwind and the symbolizer and sends the results via comms.
bool RunLibUnwindAndSymbolizer(Comms* comms); bool RunLibUnwindAndSymbolizer(Comms* comms);
absl::StatusOr<std::vector<std::string>> RunLibUnwindAndSymbolizer( std::vector<std::string> RunLibUnwindAndSymbolizer(pid_t pid,
pid_t pid, int max_frames); std::vector<uintptr_t>* ips,
int max_frames);
} // namespace sandbox2 } // namespace sandbox2