mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
Better error handling in stacktraces
PiperOrigin-RevId: 380789060 Change-Id: I655428fd45bf305f787b75cc925d31c6ab60c074
This commit is contained in:
parent
e5cfce71a3
commit
a850aa44d2
|
@ -68,6 +68,7 @@
|
|||
#include "sandboxed_api/sandbox2/util.h"
|
||||
#include "sandboxed_api/util/file_helpers.h"
|
||||
#include "sandboxed_api/util/raw_logging.h"
|
||||
#include "sandboxed_api/util/status_macros.h"
|
||||
#include "sandboxed_api/util/temp_file.h"
|
||||
|
||||
using std::string;
|
||||
|
@ -373,8 +374,15 @@ void Monitor::SetAdditionalResultInfo(std::unique_ptr<Regs> regs) {
|
|||
}
|
||||
auto* ns = policy_->GetNamespace();
|
||||
const Mounts empty_mounts;
|
||||
result_.set_stack_trace(
|
||||
GetStackTrace(result_.GetRegs(), ns ? ns->mounts() : empty_mounts));
|
||||
absl::StatusOr<std::vector<std::string>> stack_trace =
|
||||
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: [";
|
||||
for (const auto& frame : CompactStackTrace(result_.stack_trace())) {
|
||||
|
@ -936,13 +944,18 @@ void Monitor::StateProcessStopped(pid_t pid, int status) {
|
|||
if (ABSL_PREDICT_FALSE(pid == pid_ && should_dump_stack_ &&
|
||||
executor_->libunwind_sbox_for_pid_ == 0 &&
|
||||
policy_->GetNamespace())) {
|
||||
auto stack_trace = [this,
|
||||
pid]() -> absl::StatusOr<std::vector<std::string>> {
|
||||
Regs regs(pid);
|
||||
if (auto status = regs.Fetch(); !status.ok()) {
|
||||
LOG(WARNING) << "FAILED TO GET SANDBOX STACK : " << status;
|
||||
SAPI_RETURN_IF_ERROR(regs.Fetch());
|
||||
return GetStackTrace(®s, policy_->GetNamespace()->mounts());
|
||||
}();
|
||||
|
||||
if (!stack_trace.ok()) {
|
||||
LOG(WARNING) << "FAILED TO GET SANDBOX STACK : " << stack_trace.status();
|
||||
} else if (SAPI_VLOG_IS_ON(0)) {
|
||||
VLOG(0) << "SANDBOX STACK: PID: " << pid << ", [";
|
||||
for (const auto& frame :
|
||||
GetStackTrace(®s, policy_->GetNamespace()->mounts())) {
|
||||
for (const auto& frame : *stack_trace) {
|
||||
VLOG(0) << " " << frame;
|
||||
}
|
||||
VLOG(0) << "]";
|
||||
|
|
|
@ -24,8 +24,10 @@
|
|||
#include <vector>
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include "absl/cleanup/cleanup.h"
|
||||
#include "sandboxed_api/util/flag.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/numbers.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/strip.h"
|
||||
|
@ -45,6 +47,7 @@
|
|||
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
|
||||
#include "sandboxed_api/util/fileops.h"
|
||||
#include "sandboxed_api/util/path.h"
|
||||
#include "sandboxed_api/util/status_macros.h"
|
||||
|
||||
ABSL_FLAG(bool, sandbox_disable_all_stack_traces, false,
|
||||
"Completely disable stack trace collection for sandboxees");
|
||||
|
@ -53,27 +56,34 @@ ABSL_FLAG(bool, sandbox_libunwind_crash_handler, true,
|
|||
"Sandbox libunwind when handling violations (preferred)");
|
||||
|
||||
namespace sandbox2 {
|
||||
namespace {
|
||||
|
||||
namespace file = ::sapi::file;
|
||||
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 {
|
||||
public:
|
||||
static std::unique_ptr<Policy> GetPolicy(pid_t target_pid,
|
||||
const std::string& maps_file,
|
||||
const std::string& app_path,
|
||||
const std::string& exe_path,
|
||||
static absl::StatusOr<std::unique_ptr<Policy>> GetPolicy(
|
||||
pid_t target_pid, const std::string& maps_file,
|
||||
const std::string& app_path, const std::string& exe_path,
|
||||
const Mounts& mounts);
|
||||
|
||||
static bool LaunchLibunwindSandbox(const Regs* regs, const Mounts& mounts,
|
||||
UnwindResult* result);
|
||||
static absl::StatusOr<UnwindResult> LaunchLibunwindSandbox(
|
||||
const Regs* regs, const Mounts& mounts);
|
||||
};
|
||||
|
||||
std::unique_ptr<Policy> StackTracePeer::GetPolicy(pid_t target_pid,
|
||||
const std::string& maps_file,
|
||||
const std::string& app_path,
|
||||
const std::string& exe_path,
|
||||
const Mounts& mounts) {
|
||||
absl::StatusOr<std::unique_ptr<Policy>> StackTracePeer::GetPolicy(
|
||||
pid_t target_pid, const std::string& maps_file, const std::string& app_path,
|
||||
const std::string& exe_path, const Mounts& mounts) {
|
||||
PolicyBuilder builder;
|
||||
builder
|
||||
// Use the mounttree of the original executable as starting point.
|
||||
|
@ -147,23 +157,18 @@ std::unique_ptr<Policy> StackTracePeer::GetPolicy(pid_t target_pid,
|
|||
}
|
||||
}
|
||||
|
||||
auto policy = builder.TryBuild();
|
||||
if (!policy.ok()) {
|
||||
LOG(ERROR) << "Creating stack unwinder sandbox policy failed";
|
||||
return nullptr;
|
||||
}
|
||||
SAPI_ASSIGN_OR_RETURN(std::unique_ptr<Policy> policy, builder.TryBuild());
|
||||
auto keep_capabilities = absl::make_unique<std::vector<int>>();
|
||||
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
|
||||
// user namespace and will unshare() afterwards (See forkserver.cc).
|
||||
(*policy)->GetNamespace()->clone_flags_ = 0;
|
||||
return std::move(*policy);
|
||||
policy->GetNamespace()->clone_flags_ = 0;
|
||||
return std::move(policy);
|
||||
}
|
||||
|
||||
bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
||||
const Mounts& mounts,
|
||||
sandbox2::UnwindResult* result) {
|
||||
absl::StatusOr<UnwindResult> StackTracePeer::LaunchLibunwindSandbox(
|
||||
const Regs* regs, const Mounts& mounts) {
|
||||
const pid_t pid = regs->pid();
|
||||
|
||||
// Tell executor to use this special internal mode.
|
||||
|
@ -183,8 +188,8 @@ bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
|||
char unwind_temp_directory_template[] = "/tmp/.sandbox2_unwind_XXXXXX";
|
||||
char* unwind_temp_directory = mkdtemp(unwind_temp_directory_template);
|
||||
if (!unwind_temp_directory) {
|
||||
LOG(WARNING) << "Could not create temporary directory for unwinding";
|
||||
return false;
|
||||
return absl::InternalError(
|
||||
"Could not create temporary directory for unwinding");
|
||||
}
|
||||
struct UnwindTempDirectoryCleanup {
|
||||
~UnwindTempDirectoryCleanup() {
|
||||
|
@ -200,8 +205,7 @@ bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
|||
if (!file_util::fileops::CopyFile(
|
||||
file::JoinPath("/proc", absl::StrCat(pid), "maps"),
|
||||
unwind_temp_maps_path, 0400)) {
|
||||
LOG(WARNING) << "Could not copy maps file";
|
||||
return false;
|
||||
return absl::InternalError("Could not copy maps file");
|
||||
}
|
||||
|
||||
// Get path to the binary.
|
||||
|
@ -211,8 +215,7 @@ bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
|||
std::string app_path;
|
||||
std::string proc_pid_exe = file::JoinPath("/proc", absl::StrCat(pid), "exe");
|
||||
if (!file_util::fileops::ReadLinkAbsolute(proc_pid_exe, &app_path)) {
|
||||
LOG(WARNING) << "Could not obtain absolute path to the binary";
|
||||
return false;
|
||||
return absl::InternalError("Could not obtain absolute path to the binary");
|
||||
}
|
||||
|
||||
// The exe_path will have a mountable path of the application, even if it was
|
||||
|
@ -227,8 +230,7 @@ bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
|||
// Create a copy of /proc/pid/exe, mount that one.
|
||||
exe_path = file::JoinPath(unwind_temp_directory, "exe");
|
||||
if (!file_util::fileops::CopyFile(proc_pid_exe, exe_path, 0700)) {
|
||||
LOG(WARNING) << "Could not copy /proc/pid/exe";
|
||||
return false;
|
||||
return absl::InternalError("Could not copy /proc/pid/exe");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,11 +238,9 @@ bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
|||
|
||||
// Add mappings for the binary (as they might not have been added due to the
|
||||
// forkserver).
|
||||
auto policy = StackTracePeer::GetPolicy(pid, unwind_temp_maps_path, app_path,
|
||||
exe_path, mounts);
|
||||
if (!policy) {
|
||||
return false;
|
||||
}
|
||||
SAPI_ASSIGN_OR_RETURN(std::unique_ptr<Policy> policy,
|
||||
StackTracePeer::GetPolicy(pid, unwind_temp_maps_path,
|
||||
app_path, exe_path, mounts));
|
||||
Sandbox2 sandbox(std::move(executor), std::move(policy));
|
||||
|
||||
VLOG(1) << "Running libunwind sandbox";
|
||||
|
@ -253,37 +253,53 @@ bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
|||
sizeof(regs->user_regs_));
|
||||
msg.set_default_max_frames(kDefaultMaxFrames);
|
||||
|
||||
bool success = true;
|
||||
if (!comms->SendProtoBuf(msg)) {
|
||||
LOG(ERROR) << "Sending libunwind setup message failed";
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success && !comms->RecvProtoBuf(result)) {
|
||||
LOG(ERROR) << "Receiving libunwind result failed";
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
absl::Cleanup kill_sandbox = [&sandbox]() {
|
||||
sandbox.Kill();
|
||||
sandbox.AwaitResult().IgnoreResult();
|
||||
};
|
||||
|
||||
if (!comms->SendProtoBuf(msg)) {
|
||||
return absl::InternalError("Sending libunwind setup message failed");
|
||||
}
|
||||
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();
|
||||
|
||||
auto sandbox_result = sandbox.AwaitResult();
|
||||
|
||||
LOG(INFO) << "Libunwind execution status: " << sandbox_result.ToString();
|
||||
|
||||
return success && sandbox_result.final_status() == Result::OK;
|
||||
if (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()) {
|
||||
return {"[Stack traces unavailable]"};
|
||||
return absl::UnavailableError("Stack traces unavailable on Aarch64");
|
||||
}
|
||||
if (absl::GetFlag(FLAGS_sandbox_disable_all_stack_traces)) {
|
||||
return {"[Stacktraces disabled]"};
|
||||
return absl::UnavailableError("Stacktraces disabled");
|
||||
}
|
||||
if (!regs) {
|
||||
LOG(WARNING) << "Could not obtain stacktrace, regs == nullptr";
|
||||
return {"[ERROR (noregs)]"};
|
||||
return absl::InvalidArgumentError(
|
||||
"Could not obtain stacktrace, regs == nullptr");
|
||||
}
|
||||
|
||||
// Show a warning if sandboxed libunwind is requested but we're running in
|
||||
|
@ -303,17 +319,10 @@ std::vector<std::string> GetStackTrace(const Regs* regs, const Mounts& mounts) {
|
|||
return UnsafeGetStackTrace(regs->pid());
|
||||
}
|
||||
|
||||
UnwindResult res;
|
||||
if (!StackTracePeer::LaunchLibunwindSandbox(regs, mounts, &res)) {
|
||||
return {};
|
||||
}
|
||||
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);
|
||||
SAPI_ASSIGN_OR_RETURN(UnwindResult res,
|
||||
StackTracePeer::LaunchLibunwindSandbox(regs, mounts));
|
||||
return std::vector<std::string>(res.stacktrace().begin(),
|
||||
res.stacktrace().end());
|
||||
}
|
||||
|
||||
std::vector<std::string> CompactStackTrace(
|
||||
|
|
|
@ -19,15 +19,11 @@
|
|||
#ifndef SANDBOXED_API_SANDBOX2_STACK_TRACE_H_
|
||||
#define SANDBOXED_API_SANDBOX2_STACK_TRACE_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "sandboxed_api/sandbox2/mounts.h"
|
||||
#include "sandboxed_api/sandbox2/policy.h"
|
||||
#include "sandboxed_api/sandbox2/regs.h"
|
||||
|
||||
namespace sandbox2 {
|
||||
|
@ -36,11 +32,8 @@ namespace sandbox2 {
|
|||
constexpr size_t kDefaultMaxFrames = 200;
|
||||
|
||||
// Returns the stack-trace of the PID=pid, one line per frame.
|
||||
std::vector<std::string> GetStackTrace(const Regs* regs, const Mounts& mounts);
|
||||
|
||||
// Similar to GetStackTrace() but without using the sandbox to isolate
|
||||
// libunwind.
|
||||
std::vector<std::string> UnsafeGetStackTrace(pid_t pid);
|
||||
absl::StatusOr<std::vector<std::string>> GetStackTrace(const Regs* regs,
|
||||
const Mounts& mounts);
|
||||
|
||||
// Returns a stack trace that collapses duplicate stack frames and annotates
|
||||
// them with a repetition count.
|
||||
|
|
|
@ -59,7 +59,9 @@ cc_library(
|
|||
"//sandboxed_api/sandbox2/util:minielf",
|
||||
"//sandboxed_api/util:file_helpers",
|
||||
"//sandboxed_api/util:raw_logging",
|
||||
"//sandboxed_api/util:status",
|
||||
"//sandboxed_api/util:strerror",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@org_gnu_libunwind//:unwind-ptrace-wrapped",
|
||||
],
|
||||
|
|
|
@ -30,6 +30,7 @@ add_library(sandbox2_unwind STATIC
|
|||
)
|
||||
add_library(sandbox2::unwind ALIAS sandbox2_unwind)
|
||||
target_link_libraries(sandbox2_unwind PRIVATE
|
||||
absl::statusor
|
||||
absl::strings
|
||||
sandbox2::comms
|
||||
sandbox2::maps_parser
|
||||
|
@ -40,6 +41,7 @@ target_link_libraries(sandbox2_unwind PRIVATE
|
|||
sapi::base
|
||||
sapi::file_helpers
|
||||
sapi::raw_logging
|
||||
sapi::status
|
||||
unwind::unwind_ptrace_wrapped
|
||||
)
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "libunwind-ptrace.h"
|
||||
|
@ -34,6 +35,7 @@
|
|||
#include "sandboxed_api/sandbox2/util/minielf.h"
|
||||
#include "sandboxed_api/util/file_helpers.h"
|
||||
#include "sandboxed_api/util/raw_logging.h"
|
||||
#include "sandboxed_api/util/status_macros.h"
|
||||
#include "sandboxed_api/util/strerror.h"
|
||||
|
||||
namespace sandbox2 {
|
||||
|
@ -73,29 +75,77 @@ std::string GetSymbolAt(const std::map<uint64_t, std::string>& addr_to_symbol,
|
|||
return "";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
absl::StatusOr<std::map<uint64_t, std::string>> LoadSymbolsMap(pid_t pid) {
|
||||
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()));
|
||||
|
||||
std::vector<uintptr_t> GetIPList(pid_t pid, int max_frames) {
|
||||
SAPI_ASSIGN_OR_RETURN(std::vector<MapsEntry> maps,
|
||||
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;
|
||||
static unw_addr_space_t as =
|
||||
unw_create_addr_space(&_UPT_accessors, 0 /* byte order */);
|
||||
if (as == nullptr) {
|
||||
SAPI_RAW_LOG(WARNING, "unw_create_addr_space() failed");
|
||||
return {};
|
||||
return absl::InternalError("unw_create_addr_space() failed");
|
||||
}
|
||||
|
||||
std::unique_ptr<struct UPT_info, void (*)(void*)> ui(
|
||||
reinterpret_cast<struct UPT_info*>(_UPT_create(pid)), _UPT_destroy);
|
||||
if (ui == nullptr) {
|
||||
SAPI_RAW_LOG(WARNING, "_UPT_create() failed");
|
||||
return {};
|
||||
return absl::InternalError("_UPT_create() failed");
|
||||
}
|
||||
|
||||
int rc = unw_init_remote(&cursor, as, ui.get());
|
||||
if (rc < 0) {
|
||||
// Could be UNW_EINVAL (8), UNW_EUNSPEC (1) or UNW_EBADREG (3).
|
||||
SAPI_RAW_LOG(WARNING, "unw_init_remote() failed with error %d", rc);
|
||||
return {};
|
||||
return absl::InternalError(
|
||||
absl::StrCat("unw_init_remote() failed with error ", rc));
|
||||
}
|
||||
|
||||
std::vector<uintptr_t> ips;
|
||||
|
@ -120,6 +170,22 @@ std::vector<uintptr_t> GetIPList(pid_t pid, int max_frames) {
|
|||
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) {
|
||||
UnwindSetup setup;
|
||||
if (!comms->RecvProtoBuf(&setup)) {
|
||||
|
@ -128,87 +194,34 @@ bool RunLibUnwindAndSymbolizer(Comms* comms) {
|
|||
|
||||
EnablePtraceEmulationWithUserRegs(setup.regs());
|
||||
|
||||
std::vector<uintptr_t> ips;
|
||||
std::vector<std::string> stack_trace =
|
||||
RunLibUnwindAndSymbolizer(setup.pid(), &ips, setup.default_max_frames());
|
||||
absl::StatusOr<std::vector<uintptr_t>> ips =
|
||||
RunLibUnwind(setup.pid(), setup.default_max_frames());
|
||||
absl::StatusOr<std::vector<std::string>> stack_trace;
|
||||
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;
|
||||
*msg.mutable_stacktrace() = {stack_trace.begin(), stack_trace.end()};
|
||||
*msg.mutable_ip() = {ips.begin(), ips.end()};
|
||||
*msg.mutable_stacktrace() = {stack_trace->begin(), stack_trace->end()};
|
||||
*msg.mutable_ip() = {ips->begin(), ips->end()};
|
||||
return comms->SendProtoBuf(msg);
|
||||
}
|
||||
|
||||
std::vector<std::string> RunLibUnwindAndSymbolizer(pid_t pid,
|
||||
std::vector<uintptr_t>* ips,
|
||||
int max_frames) {
|
||||
*ips = GetIPList(pid, max_frames); // Uses libunwind
|
||||
|
||||
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;
|
||||
absl::StatusOr<std::vector<std::string>> RunLibUnwindAndSymbolizer(
|
||||
pid_t pid, int max_frames) {
|
||||
SAPI_ASSIGN_OR_RETURN(std::vector<uintptr_t> ips,
|
||||
RunLibUnwind(pid, max_frames));
|
||||
return SymbolizeStacktrace(pid, ips);
|
||||
}
|
||||
|
||||
} // namespace sandbox2
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
|
||||
namespace sandbox2 {
|
||||
|
@ -28,9 +29,8 @@ namespace sandbox2 {
|
|||
// Runs libunwind and the symbolizer and sends the results via comms.
|
||||
bool RunLibUnwindAndSymbolizer(Comms* comms);
|
||||
|
||||
std::vector<std::string> RunLibUnwindAndSymbolizer(pid_t pid,
|
||||
std::vector<uintptr_t>* ips,
|
||||
int max_frames);
|
||||
absl::StatusOr<std::vector<std::string>> RunLibUnwindAndSymbolizer(
|
||||
pid_t pid, int max_frames);
|
||||
|
||||
} // namespace sandbox2
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user