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/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;
|
||||||
|
@ -373,8 +374,15 @@ void Monitor::SetAdditionalResultInfo(std::unique_ptr<Regs> regs) {
|
||||||
}
|
}
|
||||||
auto* ns = policy_->GetNamespace();
|
auto* ns = policy_->GetNamespace();
|
||||||
const Mounts empty_mounts;
|
const Mounts empty_mounts;
|
||||||
result_.set_stack_trace(
|
absl::StatusOr<std::vector<std::string>> 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())) {
|
||||||
|
@ -936,13 +944,18 @@ 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);
|
||||||
if (auto status = regs.Fetch(); !status.ok()) {
|
SAPI_RETURN_IF_ERROR(regs.Fetch());
|
||||||
LOG(WARNING) << "FAILED TO GET SANDBOX STACK : " << status;
|
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)) {
|
} else if (SAPI_VLOG_IS_ON(0)) {
|
||||||
VLOG(0) << "SANDBOX STACK: PID: " << pid << ", [";
|
VLOG(0) << "SANDBOX STACK: PID: " << pid << ", [";
|
||||||
for (const auto& frame :
|
for (const auto& frame : *stack_trace) {
|
||||||
GetStackTrace(®s, policy_->GetNamespace()->mounts())) {
|
|
||||||
VLOG(0) << " " << frame;
|
VLOG(0) << " " << frame;
|
||||||
}
|
}
|
||||||
VLOG(0) << "]";
|
VLOG(0) << "]";
|
||||||
|
|
|
@ -24,8 +24,10 @@
|
||||||
#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"
|
||||||
|
@ -45,6 +47,7 @@
|
||||||
#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");
|
||||||
|
@ -53,27 +56,34 @@ 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 std::unique_ptr<Policy> GetPolicy(pid_t target_pid,
|
static absl::StatusOr<std::unique_ptr<Policy>> GetPolicy(
|
||||||
const std::string& maps_file,
|
pid_t target_pid, const std::string& maps_file,
|
||||||
const std::string& app_path,
|
const std::string& app_path, const std::string& exe_path,
|
||||||
const std::string& exe_path,
|
|
||||||
const Mounts& mounts);
|
const Mounts& mounts);
|
||||||
|
|
||||||
static bool LaunchLibunwindSandbox(const Regs* regs, const Mounts& mounts,
|
static absl::StatusOr<UnwindResult> LaunchLibunwindSandbox(
|
||||||
UnwindResult* result);
|
const Regs* regs, const Mounts& mounts);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<Policy> StackTracePeer::GetPolicy(pid_t target_pid,
|
absl::StatusOr<std::unique_ptr<Policy>> StackTracePeer::GetPolicy(
|
||||||
const std::string& maps_file,
|
pid_t target_pid, const std::string& maps_file, const std::string& app_path,
|
||||||
const std::string& app_path,
|
const std::string& exe_path, const Mounts& mounts) {
|
||||||
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.
|
||||||
|
@ -147,23 +157,18 @@ std::unique_ptr<Policy> StackTracePeer::GetPolicy(pid_t target_pid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto policy = builder.TryBuild();
|
SAPI_ASSIGN_OR_RETURN(std::unique_ptr<Policy> 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
absl::StatusOr<UnwindResult> StackTracePeer::LaunchLibunwindSandbox(
|
||||||
const Mounts& mounts,
|
const Regs* regs, 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.
|
||||||
|
@ -183,8 +188,8 @@ bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
||||||
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) {
|
||||||
LOG(WARNING) << "Could not create temporary directory for unwinding";
|
return absl::InternalError(
|
||||||
return false;
|
"Could not create temporary directory for unwinding");
|
||||||
}
|
}
|
||||||
struct UnwindTempDirectoryCleanup {
|
struct UnwindTempDirectoryCleanup {
|
||||||
~UnwindTempDirectoryCleanup() {
|
~UnwindTempDirectoryCleanup() {
|
||||||
|
@ -200,8 +205,7 @@ bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
||||||
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)) {
|
||||||
LOG(WARNING) << "Could not copy maps file";
|
return absl::InternalError("Could not copy maps file");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get path to the binary.
|
// Get path to the binary.
|
||||||
|
@ -211,8 +215,7 @@ bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
||||||
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)) {
|
||||||
LOG(WARNING) << "Could not obtain absolute path to the binary";
|
return absl::InternalError("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
|
||||||
|
@ -227,8 +230,7 @@ bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
||||||
// 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)) {
|
||||||
LOG(WARNING) << "Could not copy /proc/pid/exe";
|
return absl::InternalError("Could not copy /proc/pid/exe");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
// Add mappings for the binary (as they might not have been added due to the
|
||||||
// forkserver).
|
// forkserver).
|
||||||
auto policy = StackTracePeer::GetPolicy(pid, unwind_temp_maps_path, app_path,
|
SAPI_ASSIGN_OR_RETURN(std::unique_ptr<Policy> policy,
|
||||||
exe_path, mounts);
|
StackTracePeer::GetPolicy(pid, unwind_temp_maps_path,
|
||||||
if (!policy) {
|
app_path, exe_path, mounts));
|
||||||
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,37 +253,53 @@ bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs,
|
||||||
sizeof(regs->user_regs_));
|
sizeof(regs->user_regs_));
|
||||||
msg.set_default_max_frames(kDefaultMaxFrames);
|
msg.set_default_max_frames(kDefaultMaxFrames);
|
||||||
|
|
||||||
bool success = true;
|
absl::Cleanup kill_sandbox = [&sandbox]() {
|
||||||
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) {
|
|
||||||
sandbox.Kill();
|
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();
|
auto sandbox_result = sandbox.AwaitResult();
|
||||||
|
|
||||||
LOG(INFO) << "Libunwind execution status: " << sandbox_result.ToString();
|
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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> GetStackTrace(const Regs* regs, const Mounts& mounts) {
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {"[Stack traces unavailable]"};
|
return absl::UnavailableError("Stack traces unavailable on Aarch64");
|
||||||
}
|
}
|
||||||
if (absl::GetFlag(FLAGS_sandbox_disable_all_stack_traces)) {
|
if (absl::GetFlag(FLAGS_sandbox_disable_all_stack_traces)) {
|
||||||
return {"[Stacktraces disabled]"};
|
return absl::UnavailableError("Stacktraces disabled");
|
||||||
}
|
}
|
||||||
if (!regs) {
|
if (!regs) {
|
||||||
LOG(WARNING) << "Could not obtain stacktrace, regs == nullptr";
|
return absl::InvalidArgumentError(
|
||||||
return {"[ERROR (noregs)]"};
|
"Could not obtain stacktrace, regs == nullptr");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -303,17 +319,10 @@ std::vector<std::string> GetStackTrace(const Regs* regs, const Mounts& mounts) {
|
||||||
return UnsafeGetStackTrace(regs->pid());
|
return UnsafeGetStackTrace(regs->pid());
|
||||||
}
|
}
|
||||||
|
|
||||||
UnwindResult res;
|
SAPI_ASSIGN_OR_RETURN(UnwindResult res,
|
||||||
if (!StackTracePeer::LaunchLibunwindSandbox(regs, mounts, &res)) {
|
StackTracePeer::LaunchLibunwindSandbox(regs, mounts));
|
||||||
return {};
|
return std::vector<std::string>(res.stacktrace().begin(),
|
||||||
}
|
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(
|
||||||
|
|
|
@ -19,15 +19,11 @@
|
||||||
#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 {
|
||||||
|
@ -36,11 +32,8 @@ 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.
|
||||||
std::vector<std::string> GetStackTrace(const Regs* regs, const Mounts& mounts);
|
absl::StatusOr<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);
|
|
||||||
|
|
||||||
// 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.
|
||||||
|
|
|
@ -59,7 +59,9 @@ 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",
|
||||||
],
|
],
|
||||||
|
|
|
@ -30,6 +30,7 @@ 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
|
||||||
|
@ -40,6 +41,7 @@ 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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#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"
|
||||||
|
@ -34,6 +35,7 @@
|
||||||
#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 {
|
||||||
|
@ -73,29 +75,77 @@ std::string GetSymbolAt(const std::map<uint64_t, std::string>& addr_to_symbol,
|
||||||
return "";
|
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;
|
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) {
|
||||||
SAPI_RAW_LOG(WARNING, "unw_create_addr_space() failed");
|
return absl::InternalError("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) {
|
||||||
SAPI_RAW_LOG(WARNING, "_UPT_create() failed");
|
return absl::InternalError("_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).
|
||||||
SAPI_RAW_LOG(WARNING, "unw_init_remote() failed with error %d", rc);
|
return absl::InternalError(
|
||||||
return {};
|
absl::StrCat("unw_init_remote() failed with error ", rc));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uintptr_t> ips;
|
std::vector<uintptr_t> ips;
|
||||||
|
@ -120,6 +170,22 @@ std::vector<uintptr_t> GetIPList(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)) {
|
||||||
|
@ -128,87 +194,34 @@ bool RunLibUnwindAndSymbolizer(Comms* comms) {
|
||||||
|
|
||||||
EnablePtraceEmulationWithUserRegs(setup.regs());
|
EnablePtraceEmulationWithUserRegs(setup.regs());
|
||||||
|
|
||||||
std::vector<uintptr_t> ips;
|
absl::StatusOr<std::vector<uintptr_t>> ips =
|
||||||
std::vector<std::string> stack_trace =
|
RunLibUnwind(setup.pid(), setup.default_max_frames());
|
||||||
RunLibUnwindAndSymbolizer(setup.pid(), &ips, 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;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> RunLibUnwindAndSymbolizer(pid_t pid,
|
absl::StatusOr<std::vector<std::string>> RunLibUnwindAndSymbolizer(
|
||||||
std::vector<uintptr_t>* ips,
|
pid_t pid, int max_frames) {
|
||||||
int max_frames) {
|
SAPI_ASSIGN_OR_RETURN(std::vector<uintptr_t> ips,
|
||||||
*ips = GetIPList(pid, max_frames); // Uses libunwind
|
RunLibUnwind(pid, max_frames));
|
||||||
|
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
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#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 {
|
||||||
|
@ -28,9 +29,8 @@ 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);
|
||||||
|
|
||||||
std::vector<std::string> RunLibUnwindAndSymbolizer(pid_t pid,
|
absl::StatusOr<std::vector<std::string>> RunLibUnwindAndSymbolizer(
|
||||||
std::vector<uintptr_t>* ips,
|
pid_t pid, int max_frames);
|
||||||
int max_frames);
|
|
||||||
|
|
||||||
} // namespace sandbox2
|
} // namespace sandbox2
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user