mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
Split PtraceMonitor out of Monitor
This is a preparatory step to introduce a Sandbox2 mode that does not use ptrace. PiperOrigin-RevId: 503919613 Change-Id: I446adecc66e697c592ad938627fbfdbea12516e1
This commit is contained in:
parent
93ef7eb380
commit
f636cd86d6
|
@ -101,7 +101,8 @@ class Executor final {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Monitor;
|
friend class MonitorBase;
|
||||||
|
friend class PtraceMonitor;
|
||||||
friend class StackTracePeer;
|
friend class StackTracePeer;
|
||||||
|
|
||||||
// Internal constructor for executing libunwind on the given pid
|
// Internal constructor for executing libunwind on the given pid
|
||||||
|
|
|
@ -62,7 +62,7 @@ class IPC final {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Executor;
|
friend class Executor;
|
||||||
friend class Monitor;
|
friend class MonitorBase;
|
||||||
friend class IpcPeer; // For testing
|
friend class IpcPeer; // For testing
|
||||||
|
|
||||||
// Uses a pre-connected file descriptor.
|
// Uses a pre-connected file descriptor.
|
||||||
|
|
|
@ -248,20 +248,17 @@ void MaybeEnableTomoyoLsmWorkaround(Mounts& mounts, std::string& comms_fd_dev) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Monitor::Monitor(Executor* executor, Policy* policy, Notify* notify)
|
MonitorBase::MonitorBase(Executor* executor, Policy* policy, Notify* notify)
|
||||||
: executor_(executor),
|
: executor_(executor),
|
||||||
notify_(notify),
|
notify_(notify),
|
||||||
policy_(policy),
|
policy_(policy),
|
||||||
// NOLINTNEXTLINE clang-diagnostic-deprecated-declarations
|
// NOLINTNEXTLINE clang-diagnostic-deprecated-declarations
|
||||||
comms_(executor_->ipc()->comms()),
|
comms_(executor_->ipc()->comms()),
|
||||||
ipc_(executor_->ipc()),
|
ipc_(executor_->ipc()) {
|
||||||
wait_for_execve_(executor->enable_sandboxing_pre_execve_) {
|
|
||||||
// It's a pre-connected Comms channel, no need to accept new connection.
|
// It's a pre-connected Comms channel, no need to accept new connection.
|
||||||
CHECK(comms_->IsConnected());
|
CHECK(comms_->IsConnected());
|
||||||
std::string path =
|
std::string path =
|
||||||
absl::GetFlag(FLAGS_sandbox2_danger_danger_permit_all_and_log);
|
absl::GetFlag(FLAGS_sandbox2_danger_danger_permit_all_and_log);
|
||||||
external_kill_request_flag_.test_and_set(std::memory_order_relaxed);
|
|
||||||
dump_stack_request_flag_.test_and_set(std::memory_order_relaxed);
|
|
||||||
if (!path.empty()) {
|
if (!path.empty()) {
|
||||||
log_file_ = std::fopen(path.c_str(), "a+");
|
log_file_ = std::fopen(path.c_str(), "a+");
|
||||||
PCHECK(log_file_ != nullptr) << "Failed to open log file '" << path << "'";
|
PCHECK(log_file_ != nullptr) << "Failed to open log file '" << path << "'";
|
||||||
|
@ -274,7 +271,21 @@ Monitor::Monitor(Executor* executor, Policy* policy, Notify* notify)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Monitor::~Monitor() {
|
PtraceMonitor::PtraceMonitor(Executor* executor, Policy* policy, Notify* notify)
|
||||||
|
: MonitorBase(executor, policy, notify),
|
||||||
|
wait_for_execve_(executor->enable_sandboxing_pre_execve_),
|
||||||
|
uses_custom_forkserver_(executor_->fork_client_ != nullptr) {
|
||||||
|
if (executor_->limits()->wall_time_limit() != absl::ZeroDuration()) {
|
||||||
|
auto deadline = absl::Now() + executor_->limits()->wall_time_limit();
|
||||||
|
LOG(INFO) << executor_->limits()->wall_time_limit();
|
||||||
|
deadline_millis_.store(absl::ToUnixMillis(deadline),
|
||||||
|
std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
external_kill_request_flag_.test_and_set(std::memory_order_relaxed);
|
||||||
|
dump_stack_request_flag_.test_and_set(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
MonitorBase::~MonitorBase() {
|
||||||
if (!comms_fd_dev_.empty()) {
|
if (!comms_fd_dev_.empty()) {
|
||||||
std::remove(comms_fd_dev_.c_str());
|
std::remove(comms_fd_dev_.c_str());
|
||||||
}
|
}
|
||||||
|
@ -297,30 +308,27 @@ void LogContainer(const std::vector<std::string>& container) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void Monitor::Run() {
|
void MonitorBase::OnDone() {
|
||||||
absl::Cleanup setup_notify = [this] { setup_notification_.Notify(); };
|
if (done_notification_.HasBeenNotified()) {
|
||||||
|
|
||||||
absl::Cleanup monitor_cleanup = [this] {
|
|
||||||
getrusage(RUSAGE_THREAD, result_.GetRUsageMonitor());
|
|
||||||
notify_->EventFinished(result_);
|
|
||||||
ipc_->InternalCleanupFdMap();
|
|
||||||
done_notification_.Notify();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (executor_->limits()->wall_time_limit() != absl::ZeroDuration()) {
|
|
||||||
auto deadline = absl::Now() + executor_->limits()->wall_time_limit();
|
|
||||||
deadline_millis_.store(absl::ToUnixMillis(deadline),
|
|
||||||
std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// It'd be costly to initialize the sigset_t for each sigtimedwait()
|
|
||||||
// invocation, so do it once per Monitor.
|
|
||||||
sigset_t sigtimedwait_sset;
|
|
||||||
if (!InitSetupSignals(&sigtimedwait_sset)) {
|
|
||||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_SIGNALS);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notify_->EventFinished(result_);
|
||||||
|
ipc_->InternalCleanupFdMap();
|
||||||
|
done_notification_.Notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MonitorBase::Launch() {
|
||||||
|
|
||||||
|
absl::Cleanup process_cleanup = [this] {
|
||||||
|
if (process_.init_pid > 0) {
|
||||||
|
kill(process_.init_pid, SIGKILL);
|
||||||
|
} else if (process_.main_pid > 0) {
|
||||||
|
kill(process_.main_pid, SIGKILL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
absl::Cleanup monitor_done = [this] { OnDone(); };
|
||||||
|
|
||||||
Namespace* ns = policy_->GetNamespace();
|
Namespace* ns = policy_->GetNamespace();
|
||||||
if (SAPI_VLOG_IS_ON(1) && ns != nullptr) {
|
if (SAPI_VLOG_IS_ON(1) && ns != nullptr) {
|
||||||
std::vector<std::string> outside_entries;
|
std::vector<std::string> outside_entries;
|
||||||
|
@ -344,7 +352,6 @@ void Monitor::Run() {
|
||||||
|
|
||||||
// Get PID of the sandboxee.
|
// Get PID of the sandboxee.
|
||||||
bool should_have_init = ns && (ns->GetCloneFlags() & CLONE_NEWPID);
|
bool should_have_init = ns && (ns->GetCloneFlags() & CLONE_NEWPID);
|
||||||
uses_custom_forkserver_ = executor_->fork_client_ != nullptr;
|
|
||||||
absl::StatusOr<Executor::Process> process =
|
absl::StatusOr<Executor::Process> process =
|
||||||
executor_->StartSubProcess(clone_flags, ns, policy_->capabilities());
|
executor_->StartSubProcess(clone_flags, ns, policy_->capabilities());
|
||||||
|
|
||||||
|
@ -354,24 +361,14 @@ void Monitor::Run() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_ = process->main_pid;
|
process_ = *std::move(process);
|
||||||
|
|
||||||
if (process->init_pid > 0) {
|
if (process_.main_pid <= 0 || (should_have_init && process_.main_pid <= 0)) {
|
||||||
if (ptrace(PTRACE_SEIZE, process->init_pid, 0, PTRACE_O_EXITKILL) != 0) {
|
|
||||||
if (errno == ESRCH) {
|
|
||||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_PTRACE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PLOG(FATAL) << "attaching to init process failed";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pid_ <= 0 || (should_have_init && process->init_pid <= 0)) {
|
|
||||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_SUBPROCESS);
|
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_SUBPROCESS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!notify_->EventStarted(pid_, comms_)) {
|
if (!notify_->EventStarted(process_.main_pid, comms_)) {
|
||||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_NOTIFY);
|
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_NOTIFY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -395,35 +392,37 @@ void Monitor::Run() {
|
||||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_LIMITS);
|
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_LIMITS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// This call should be the last in the init sequence, because it can cause the
|
std::move(process_cleanup).Cancel();
|
||||||
// sandboxee to enter ptrace-stopped state, in which it will not be able to
|
|
||||||
// send any messages over the Comms channel.
|
|
||||||
if (!InitPtraceAttach()) {
|
|
||||||
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_PTRACE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tell the parent thread (Sandbox2 object) that we're done with the initial
|
RunInternal();
|
||||||
// set-up process of the sandboxee.
|
std::move(monitor_done).Cancel();
|
||||||
std::move(setup_notify).Invoke();
|
|
||||||
|
|
||||||
MainLoop(&sigtimedwait_sset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monitor::IsActivelyMonitoring() {
|
absl::StatusOr<Result> MonitorBase::AwaitResultWithTimeout(
|
||||||
|
absl::Duration timeout) {
|
||||||
|
auto done = done_notification_.WaitForNotificationWithTimeout(timeout);
|
||||||
|
if (!done) {
|
||||||
|
return absl::DeadlineExceededError("Sandbox did not finish within timeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
Join();
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PtraceMonitor::IsActivelyMonitoring() {
|
||||||
// If we're still waiting for execve(), then we allow all syscalls.
|
// If we're still waiting for execve(), then we allow all syscalls.
|
||||||
return !wait_for_execve_;
|
return !wait_for_execve_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::SetActivelyMonitoring() { wait_for_execve_ = false; }
|
void PtraceMonitor::SetActivelyMonitoring() { wait_for_execve_ = false; }
|
||||||
|
|
||||||
void Monitor::SetExitStatusCode(Result::StatusEnum final_status,
|
void MonitorBase::SetExitStatusCode(Result::StatusEnum final_status,
|
||||||
uintptr_t reason_code) {
|
uintptr_t reason_code) {
|
||||||
CHECK(result_.final_status() == Result::UNSET);
|
CHECK(result_.final_status() == Result::UNSET);
|
||||||
result_.SetExitStatusCode(final_status, reason_code);
|
result_.SetExitStatusCode(final_status, reason_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monitor::StackTraceCollectionPossible() {
|
bool PtraceMonitor::StackTraceCollectionPossible() const {
|
||||||
// Only get the stacktrace if we are not in the libunwind sandbox (avoid
|
// Only get the stacktrace if we are not in the libunwind sandbox (avoid
|
||||||
// recursion).
|
// recursion).
|
||||||
if ((policy_->GetNamespace() ||
|
if ((policy_->GetNamespace() ||
|
||||||
|
@ -438,7 +437,7 @@ bool Monitor::StackTraceCollectionPossible() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monitor::ShouldCollectStackTrace() {
|
bool PtraceMonitor::ShouldCollectStackTrace() const {
|
||||||
if (!StackTraceCollectionPossible()) {
|
if (!StackTraceCollectionPossible()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -458,7 +457,7 @@ bool Monitor::ShouldCollectStackTrace() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::StatusOr<std::vector<std::string>> Monitor::GetAndLogStackTrace(
|
absl::StatusOr<std::vector<std::string>> PtraceMonitor::GetAndLogStackTrace(
|
||||||
const Regs* regs) {
|
const Regs* regs) {
|
||||||
SAPI_ASSIGN_OR_RETURN(
|
SAPI_ASSIGN_OR_RETURN(
|
||||||
std::vector<std::string> stack_trace,
|
std::vector<std::string> stack_trace,
|
||||||
|
@ -473,11 +472,11 @@ absl::StatusOr<std::vector<std::string>> Monitor::GetAndLogStackTrace(
|
||||||
return stack_trace;
|
return stack_trace;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::SetAdditionalResultInfo(std::unique_ptr<Regs> regs) {
|
void PtraceMonitor::SetAdditionalResultInfo(std::unique_ptr<Regs> regs) {
|
||||||
pid_t pid = regs->pid();
|
pid_t pid = regs->pid();
|
||||||
result_.SetRegs(std::move(regs));
|
result_.SetRegs(std::move(regs));
|
||||||
result_.SetProgName(util::GetProgName(pid));
|
result_.SetProgName(util::GetProgName(pid));
|
||||||
result_.SetProcMaps(ReadProcMaps(pid_));
|
result_.SetProcMaps(ReadProcMaps(pid));
|
||||||
if (!ShouldCollectStackTrace()) {
|
if (!ShouldCollectStackTrace()) {
|
||||||
VLOG(1) << "Stack traces have been disabled";
|
VLOG(1) << "Stack traces have been disabled";
|
||||||
return;
|
return;
|
||||||
|
@ -492,19 +491,19 @@ void Monitor::SetAdditionalResultInfo(std::unique_ptr<Regs> regs) {
|
||||||
result_.set_stack_trace(*stack_trace);
|
result_.set_stack_trace(*stack_trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monitor::KillSandboxee() {
|
bool PtraceMonitor::KillSandboxee() {
|
||||||
VLOG(1) << "Sending SIGKILL to the PID: " << pid_;
|
VLOG(1) << "Sending SIGKILL to the PID: " << process_.main_pid;
|
||||||
if (kill(pid_, SIGKILL) != 0) {
|
if (kill(process_.main_pid, SIGKILL) != 0) {
|
||||||
PLOG(ERROR) << "Could not send SIGKILL to PID " << pid_;
|
PLOG(ERROR) << "Could not send SIGKILL to PID " << process_.main_pid;
|
||||||
SetExitStatusCode(Result::INTERNAL_ERROR, Result::FAILED_KILL);
|
SetExitStatusCode(Result::INTERNAL_ERROR, Result::FAILED_KILL);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monitor::InterruptSandboxee() {
|
bool PtraceMonitor::InterruptSandboxee() {
|
||||||
if (ptrace(PTRACE_INTERRUPT, pid_, 0, 0) == -1) {
|
if (ptrace(PTRACE_INTERRUPT, process_.main_pid, 0, 0) == -1) {
|
||||||
PLOG(ERROR) << "Could not send interrupt to pid=" << pid_;
|
PLOG(ERROR) << "Could not send interrupt to pid=" << process_.main_pid;
|
||||||
SetExitStatusCode(Result::INTERNAL_ERROR, Result::FAILED_INTERRUPT);
|
SetExitStatusCode(Result::INTERNAL_ERROR, Result::FAILED_INTERRUPT);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -514,13 +513,64 @@ bool Monitor::InterruptSandboxee() {
|
||||||
// Not defined in glibc.
|
// Not defined in glibc.
|
||||||
#define __WPTRACEEVENT(x) ((x & 0xff0000) >> 16)
|
#define __WPTRACEEVENT(x) ((x & 0xff0000) >> 16)
|
||||||
|
|
||||||
void Monitor::MainLoop(sigset_t* sset) {
|
void PtraceMonitor::NotifyMonitor() {
|
||||||
|
absl::ReaderMutexLock lock(¬ify_mutex_);
|
||||||
|
if (thread_ != nullptr) {
|
||||||
|
pthread_kill(thread_->native_handle(), SIGCHLD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PtraceMonitor::Join() {
|
||||||
|
absl::MutexLock lock(¬ify_mutex_);
|
||||||
|
if (thread_) {
|
||||||
|
thread_->join();
|
||||||
|
CHECK(IsDone()) << "Monitor did not terminate";
|
||||||
|
VLOG(1) << "Final execution status: " << result_.ToString();
|
||||||
|
CHECK(result_.final_status() != Result::UNSET);
|
||||||
|
thread_.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PtraceMonitor::RunInternal() {
|
||||||
|
thread_ = std::make_unique<std::thread>(&PtraceMonitor::Run, this);
|
||||||
|
|
||||||
|
// Wait for the Monitor to set-up the sandboxee correctly (or fail while
|
||||||
|
// doing that). From here on, it is safe to use the IPC object for
|
||||||
|
// non-sandbox-related data exchange.
|
||||||
|
setup_notification_.WaitForNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PtraceMonitor::Run() {
|
||||||
|
absl::Cleanup monitor_done = [this] {
|
||||||
|
getrusage(RUSAGE_THREAD, result_.GetRUsageMonitor());
|
||||||
|
OnDone();
|
||||||
|
};
|
||||||
|
|
||||||
|
absl::Cleanup setup_notify = [this] { setup_notification_.Notify(); };
|
||||||
|
// It'd be costly to initialize the sigset_t for each sigtimedwait()
|
||||||
|
// invocation, so do it once per Monitor.
|
||||||
|
if (!InitSetupSignals()) {
|
||||||
|
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_SIGNALS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// This call should be the last in the init sequence, because it can cause the
|
||||||
|
// sandboxee to enter ptrace-stopped state, in which it will not be able to
|
||||||
|
// send any messages over the Comms channel.
|
||||||
|
if (!InitPtraceAttach()) {
|
||||||
|
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_PTRACE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the parent thread (Sandbox2 object) that we're done with the initial
|
||||||
|
// set-up process of the sandboxee.
|
||||||
|
std::move(setup_notify).Invoke();
|
||||||
|
|
||||||
bool sandboxee_exited = false;
|
bool sandboxee_exited = false;
|
||||||
PidWaiter pid_waiter(pid_);
|
PidWaiter pid_waiter(process_.main_pid);
|
||||||
int status;
|
int status;
|
||||||
// All possible still running children of main process, will be killed due to
|
// All possible still running children of main process, will be killed due to
|
||||||
// PTRACE_O_EXITKILL ptrace() flag.
|
// PTRACE_O_EXITKILL ptrace() flag.
|
||||||
while (result_.final_status() == Result::UNSET) {
|
while (result().final_status() == Result::UNSET) {
|
||||||
int64_t deadline = deadline_millis_.load(std::memory_order_relaxed);
|
int64_t deadline = deadline_millis_.load(std::memory_order_relaxed);
|
||||||
if (deadline != 0 && absl::Now() >= absl::FromUnixMillis(deadline)) {
|
if (deadline != 0 && absl::Now() >= absl::FromUnixMillis(deadline)) {
|
||||||
VLOG(1) << "Sandbox process hit timeout due to the walltime timer";
|
VLOG(1) << "Sandbox process hit timeout due to the walltime timer";
|
||||||
|
@ -557,7 +607,7 @@ void Monitor::MainLoop(sigset_t* sset) {
|
||||||
pid_t ret = pid_waiter.Wait(&status);
|
pid_t ret = pid_waiter.Wait(&status);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
constexpr timespec ts = {kWakeUpPeriodSec, kWakeUpPeriodNSec};
|
constexpr timespec ts = {kWakeUpPeriodSec, kWakeUpPeriodNSec};
|
||||||
int signo = sigtimedwait(sset, nullptr, &ts);
|
int signo = sigtimedwait(&sset_, nullptr, &ts);
|
||||||
LOG_IF(ERROR, signo != -1 && signo != SIGCHLD)
|
LOG_IF(ERROR, signo != -1 && signo != SIGCHLD)
|
||||||
<< "Unknown signal received: " << signo;
|
<< "Unknown signal received: " << signo;
|
||||||
continue;
|
continue;
|
||||||
|
@ -582,7 +632,7 @@ void Monitor::MainLoop(sigset_t* sset) {
|
||||||
// That's the main process, set the exit code, and exit. It will kill
|
// That's the main process, set the exit code, and exit. It will kill
|
||||||
// all remaining processes (if there are any) because of the
|
// all remaining processes (if there are any) because of the
|
||||||
// PTRACE_O_EXITKILL ptrace() flag.
|
// PTRACE_O_EXITKILL ptrace() flag.
|
||||||
if (ret == pid_) {
|
if (ret == process_.main_pid) {
|
||||||
if (IsActivelyMonitoring()) {
|
if (IsActivelyMonitoring()) {
|
||||||
SetExitStatusCode(Result::OK, WEXITSTATUS(status));
|
SetExitStatusCode(Result::OK, WEXITSTATUS(status));
|
||||||
} else {
|
} else {
|
||||||
|
@ -597,7 +647,7 @@ void Monitor::MainLoop(sigset_t* sset) {
|
||||||
// actual signal death. This may be changed in the future;
|
// actual signal death. This may be changed in the future;
|
||||||
VLOG(1) << "PID: " << ret << " terminated with signal: "
|
VLOG(1) << "PID: " << ret << " terminated with signal: "
|
||||||
<< util::GetSignalName(WTERMSIG(status));
|
<< util::GetSignalName(WTERMSIG(status));
|
||||||
if (ret == pid_) {
|
if (ret == process_.main_pid) {
|
||||||
if (network_violation_) {
|
if (network_violation_) {
|
||||||
SetExitStatusCode(Result::VIOLATION, Result::VIOLATION_NETWORK);
|
SetExitStatusCode(Result::VIOLATION, Result::VIOLATION_NETWORK);
|
||||||
result_.SetNetworkViolation(network_proxy_server_->violation_msg_);
|
result_.SetNetworkViolation(network_proxy_server_->violation_msg_);
|
||||||
|
@ -627,7 +677,7 @@ void Monitor::MainLoop(sigset_t* sset) {
|
||||||
absl::GetFlag(FLAGS_sandbox2_log_all_stack_traces);
|
absl::GetFlag(FLAGS_sandbox2_log_all_stack_traces);
|
||||||
if (!log_stack_traces) {
|
if (!log_stack_traces) {
|
||||||
// Try to make sure main pid is killed and reaped
|
// Try to make sure main pid is killed and reaped
|
||||||
kill(pid_, SIGKILL);
|
kill(process_.main_pid, SIGKILL);
|
||||||
}
|
}
|
||||||
constexpr auto kGracefullExitTimeout = absl::Milliseconds(200);
|
constexpr auto kGracefullExitTimeout = absl::Milliseconds(200);
|
||||||
auto deadline = absl::Now() + kGracefullExitTimeout;
|
auto deadline = absl::Now() + kGracefullExitTimeout;
|
||||||
|
@ -648,14 +698,14 @@ void Monitor::MainLoop(sigset_t* sset) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!log_stack_traces && ret == pid_ &&
|
if (!log_stack_traces && ret == process_.main_pid &&
|
||||||
(WIFSIGNALED(status) || WIFEXITED(status))) {
|
(WIFSIGNALED(status) || WIFEXITED(status))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
auto ts = absl::ToTimespec(left);
|
auto ts = absl::ToTimespec(left);
|
||||||
sigtimedwait(sset, nullptr, &ts);
|
sigtimedwait(&sset_, nullptr, &ts);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,13 +722,13 @@ void Monitor::MainLoop(sigset_t* sset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!log_stack_traces) {
|
if (!log_stack_traces) {
|
||||||
kill(pid_, SIGKILL);
|
kill(process_.main_pid, SIGKILL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::LogStackTraceOfPid(pid_t pid) {
|
void PtraceMonitor::LogStackTraceOfPid(pid_t pid) {
|
||||||
if (!StackTraceCollectionPossible()) {
|
if (!StackTraceCollectionPossible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -695,19 +745,19 @@ void Monitor::LogStackTraceOfPid(pid_t pid) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monitor::InitSetupSignals(sigset_t* sset) {
|
bool PtraceMonitor::InitSetupSignals() {
|
||||||
if (sigemptyset(sset) == -1) {
|
if (sigemptyset(&sset_) == -1) {
|
||||||
PLOG(ERROR) << "sigemptyset()";
|
PLOG(ERROR) << "sigemptyset()";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sigtimedwait will react (wake-up) to arrival of this signal.
|
// sigtimedwait will react (wake-up) to arrival of this signal.
|
||||||
if (sigaddset(sset, SIGCHLD) == -1) {
|
if (sigaddset(&sset_, SIGCHLD) == -1) {
|
||||||
PLOG(ERROR) << "sigaddset(SIGCHLD)";
|
PLOG(ERROR) << "sigaddset(SIGCHLD)";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pthread_sigmask(SIG_BLOCK, sset, nullptr) == -1) {
|
if (pthread_sigmask(SIG_BLOCK, &sset_, nullptr) == -1) {
|
||||||
PLOG(ERROR) << "pthread_sigmask(SIG_BLOCK, SIGCHLD)";
|
PLOG(ERROR) << "pthread_sigmask(SIG_BLOCK, SIGCHLD)";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -715,7 +765,7 @@ bool Monitor::InitSetupSignals(sigset_t* sset) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monitor::InitSendPolicy() {
|
bool MonitorBase::InitSendPolicy() {
|
||||||
if (!policy_->SendPolicy(comms_)) {
|
if (!policy_->SendPolicy(comms_)) {
|
||||||
LOG(ERROR) << "Couldn't send policy";
|
LOG(ERROR) << "Couldn't send policy";
|
||||||
return false;
|
return false;
|
||||||
|
@ -724,7 +774,7 @@ bool Monitor::InitSendPolicy() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monitor::InitSendCwd() {
|
bool MonitorBase::InitSendCwd() {
|
||||||
if (!comms_->SendString(executor_->cwd_)) {
|
if (!comms_->SendString(executor_->cwd_)) {
|
||||||
PLOG(ERROR) << "Couldn't send cwd";
|
PLOG(ERROR) << "Couldn't send cwd";
|
||||||
return false;
|
return false;
|
||||||
|
@ -733,8 +783,8 @@ bool Monitor::InitSendCwd() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monitor::InitApplyLimit(pid_t pid, int resource,
|
bool MonitorBase::InitApplyLimit(pid_t pid, int resource,
|
||||||
const rlimit64& rlim) const {
|
const rlimit64& rlim) const {
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
using RlimitResource = int;
|
using RlimitResource = int;
|
||||||
#else
|
#else
|
||||||
|
@ -765,18 +815,20 @@ bool Monitor::InitApplyLimit(pid_t pid, int resource,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monitor::InitApplyLimits() {
|
bool MonitorBase::InitApplyLimits() {
|
||||||
Limits* limits = executor_->limits();
|
Limits* limits = executor_->limits();
|
||||||
return InitApplyLimit(pid_, RLIMIT_AS, limits->rlimit_as()) &&
|
return InitApplyLimit(process_.main_pid, RLIMIT_AS, limits->rlimit_as()) &&
|
||||||
InitApplyLimit(pid_, RLIMIT_CPU, limits->rlimit_cpu()) &&
|
InitApplyLimit(process_.main_pid, RLIMIT_CPU, limits->rlimit_cpu()) &&
|
||||||
InitApplyLimit(pid_, RLIMIT_FSIZE, limits->rlimit_fsize()) &&
|
InitApplyLimit(process_.main_pid, RLIMIT_FSIZE,
|
||||||
InitApplyLimit(pid_, RLIMIT_NOFILE, limits->rlimit_nofile()) &&
|
limits->rlimit_fsize()) &&
|
||||||
InitApplyLimit(pid_, RLIMIT_CORE, limits->rlimit_core());
|
InitApplyLimit(process_.main_pid, RLIMIT_NOFILE,
|
||||||
|
limits->rlimit_nofile()) &&
|
||||||
|
InitApplyLimit(process_.main_pid, RLIMIT_CORE, limits->rlimit_core());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monitor::InitSendIPC() { return ipc_->SendFdsOverComms(); }
|
bool MonitorBase::InitSendIPC() { return ipc_->SendFdsOverComms(); }
|
||||||
|
|
||||||
bool Monitor::WaitForSandboxReady() {
|
bool MonitorBase::WaitForSandboxReady() {
|
||||||
uint32_t tmp;
|
uint32_t tmp;
|
||||||
if (!comms_->RecvUint32(&tmp)) {
|
if (!comms_->RecvUint32(&tmp)) {
|
||||||
LOG(ERROR) << "Couldn't receive 'Client::kClient2SandboxReady' message";
|
LOG(ERROR) << "Couldn't receive 'Client::kClient2SandboxReady' message";
|
||||||
|
@ -790,12 +842,22 @@ bool Monitor::WaitForSandboxReady() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monitor::InitPtraceAttach() {
|
bool PtraceMonitor::InitPtraceAttach() {
|
||||||
sanitizer::WaitForSanitizer();
|
sanitizer::WaitForSanitizer();
|
||||||
|
|
||||||
|
if (process_.init_pid > 0) {
|
||||||
|
if (ptrace(PTRACE_SEIZE, process_.init_pid, 0, PTRACE_O_EXITKILL) != 0) {
|
||||||
|
if (errno != ESRCH) {
|
||||||
|
PLOG(ERROR) << "attaching to init process failed";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get a list of tasks.
|
// Get a list of tasks.
|
||||||
absl::flat_hash_set<int> tasks;
|
absl::flat_hash_set<int> tasks;
|
||||||
if (auto task_list = sanitizer::GetListOfTasks(pid_); task_list.ok()) {
|
if (auto task_list = sanitizer::GetListOfTasks(process_.main_pid);
|
||||||
|
task_list.ok()) {
|
||||||
tasks = *std::move(task_list);
|
tasks = *std::move(task_list);
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "Could not get list of tasks: "
|
LOG(ERROR) << "Could not get list of tasks: "
|
||||||
|
@ -803,14 +865,16 @@ bool Monitor::InitPtraceAttach() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tasks.find(pid_) == tasks.end()) {
|
if (tasks.find(process_.main_pid) == tasks.end()) {
|
||||||
LOG(ERROR) << "The pid " << pid_ << " was not found in its own tasklist.";
|
LOG(ERROR) << "The pid " << process_.main_pid
|
||||||
|
<< " was not found in its own tasklist.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// With TSYNC, we can allow threads: seccomp applies to all threads.
|
// With TSYNC, we can allow threads: seccomp applies to all threads.
|
||||||
if (tasks.size() > 1) {
|
if (tasks.size() > 1) {
|
||||||
LOG(WARNING) << "PID " << pid_ << " has " << tasks.size() << " threads,"
|
LOG(WARNING) << "PID " << process_.main_pid << " has " << tasks.size()
|
||||||
|
<< " threads,"
|
||||||
<< " at the time of call to SandboxMeHere. If you are seeing"
|
<< " at the time of call to SandboxMeHere. If you are seeing"
|
||||||
<< " more sandbox violations than expected, this might be"
|
<< " more sandbox violations than expected, this might be"
|
||||||
<< " the reason why"
|
<< " the reason why"
|
||||||
|
@ -874,7 +938,8 @@ bool Monitor::InitPtraceAttach() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a list of tasks after attaching.
|
// Get a list of tasks after attaching.
|
||||||
if (auto tasks_list = sanitizer::GetListOfTasks(pid_); tasks_list.ok()) {
|
if (auto tasks_list = sanitizer::GetListOfTasks(process_.main_pid);
|
||||||
|
tasks_list.ok()) {
|
||||||
tasks = *std::move(tasks_list);
|
tasks = *std::move(tasks_list);
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "Could not get list of tasks: "
|
LOG(ERROR) << "Could not get list of tasks: "
|
||||||
|
@ -884,7 +949,7 @@ bool Monitor::InitPtraceAttach() {
|
||||||
|
|
||||||
// Check that we attached to all the threads
|
// Check that we attached to all the threads
|
||||||
if (tasks_attached != tasks) {
|
if (tasks_attached != tasks) {
|
||||||
LOG(ERROR) << "The pid " << pid_
|
LOG(ERROR) << "The pid " << process_.main_pid
|
||||||
<< " spawned new threads while we were trying to attach to it.";
|
<< " spawned new threads while we were trying to attach to it.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -892,7 +957,7 @@ bool Monitor::InitPtraceAttach() {
|
||||||
// No glibc wrapper for gettid - see 'man gettid'.
|
// No glibc wrapper for gettid - see 'man gettid'.
|
||||||
VLOG(1) << "Monitor (PID: " << getpid()
|
VLOG(1) << "Monitor (PID: " << getpid()
|
||||||
<< ", TID: " << util::Syscall(__NR_gettid)
|
<< ", TID: " << util::Syscall(__NR_gettid)
|
||||||
<< ") attached to PID: " << pid_;
|
<< ") attached to PID: " << process_.main_pid;
|
||||||
|
|
||||||
// Technically, the sandboxee can be in a ptrace-stopped state right now,
|
// Technically, the sandboxee can be in a ptrace-stopped state right now,
|
||||||
// because some signal could have arrived in the meantime. Yet, this
|
// because some signal could have arrived in the meantime. Yet, this
|
||||||
|
@ -908,7 +973,7 @@ bool Monitor::InitPtraceAttach() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::ActionProcessSyscall(Regs* regs, const Syscall& syscall) {
|
void PtraceMonitor::ActionProcessSyscall(Regs* regs, const Syscall& syscall) {
|
||||||
// If the sandboxing is not enabled yet, allow the first __NR_execveat.
|
// If the sandboxing is not enabled yet, allow the first __NR_execveat.
|
||||||
if (syscall.nr() == __NR_execveat && !IsActivelyMonitoring()) {
|
if (syscall.nr() == __NR_execveat && !IsActivelyMonitoring()) {
|
||||||
VLOG(1) << "[PERMITTED/BEFORE_EXECVEAT]: "
|
VLOG(1) << "[PERMITTED/BEFORE_EXECVEAT]: "
|
||||||
|
@ -955,8 +1020,8 @@ void Monitor::ActionProcessSyscall(Regs* regs, const Syscall& syscall) {
|
||||||
ActionProcessSyscallViolation(regs, syscall, kSyscallViolation);
|
ActionProcessSyscallViolation(regs, syscall, kSyscallViolation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::ActionProcessSyscallViolation(Regs* regs, const Syscall& syscall,
|
void PtraceMonitor::ActionProcessSyscallViolation(
|
||||||
ViolationType violation_type) {
|
Regs* regs, const Syscall& syscall, ViolationType violation_type) {
|
||||||
LogSyscallViolation(syscall);
|
LogSyscallViolation(syscall);
|
||||||
notify_->EventSyscallViolation(syscall, violation_type);
|
notify_->EventSyscallViolation(syscall, violation_type);
|
||||||
SetExitStatusCode(Result::VIOLATION, syscall.nr());
|
SetExitStatusCode(Result::VIOLATION, syscall.nr());
|
||||||
|
@ -970,7 +1035,7 @@ void Monitor::ActionProcessSyscallViolation(Regs* regs, const Syscall& syscall,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::LogSyscallViolation(const Syscall& syscall) const {
|
void MonitorBase::LogSyscallViolation(const Syscall& syscall) const {
|
||||||
// Do not unwind libunwind.
|
// Do not unwind libunwind.
|
||||||
if (executor_->libunwind_sbox_for_pid_ != 0) {
|
if (executor_->libunwind_sbox_for_pid_ != 0) {
|
||||||
LOG(ERROR) << "Sandbox violation during execution of libunwind: "
|
LOG(ERROR) << "Sandbox violation during execution of libunwind: "
|
||||||
|
@ -992,7 +1057,7 @@ void Monitor::LogSyscallViolation(const Syscall& syscall) const {
|
||||||
LogSyscallViolationExplanation(syscall);
|
LogSyscallViolationExplanation(syscall);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::EventPtraceSeccomp(pid_t pid, int event_msg) {
|
void PtraceMonitor::EventPtraceSeccomp(pid_t pid, int event_msg) {
|
||||||
if (event_msg < sapi::cpu::Architecture::kUnknown ||
|
if (event_msg < sapi::cpu::Architecture::kUnknown ||
|
||||||
event_msg > sapi::cpu::Architecture::kMax) {
|
event_msg > sapi::cpu::Architecture::kMax) {
|
||||||
// We've observed that, if the process has exited, the event_msg may contain
|
// We've observed that, if the process has exited, the event_msg may contain
|
||||||
|
@ -1032,7 +1097,7 @@ void Monitor::EventPtraceSeccomp(pid_t pid, int event_msg) {
|
||||||
ActionProcessSyscall(®s, syscall);
|
ActionProcessSyscall(®s, syscall);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::EventSyscallExit(pid_t pid) {
|
void PtraceMonitor::EventSyscallExit(pid_t pid) {
|
||||||
// Check that the monitor wants to inspect the current syscall's return value.
|
// Check that the monitor wants to inspect the current syscall's return value.
|
||||||
auto index = syscalls_in_progress_.find(pid);
|
auto index = syscalls_in_progress_.find(pid);
|
||||||
if (index == syscalls_in_progress_.end()) {
|
if (index == syscalls_in_progress_.end()) {
|
||||||
|
@ -1058,7 +1123,7 @@ void Monitor::EventSyscallExit(pid_t pid) {
|
||||||
ContinueProcess(pid, 0);
|
ContinueProcess(pid, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::EventPtraceNewProcess(pid_t pid, int event_msg) {
|
void PtraceMonitor::EventPtraceNewProcess(pid_t pid, int event_msg) {
|
||||||
// ptrace doesn't issue syscall-exit-stops for successful fork/vfork/clone
|
// ptrace doesn't issue syscall-exit-stops for successful fork/vfork/clone
|
||||||
// system calls. Check if the monitor wanted to inspect the syscall's return
|
// system calls. Check if the monitor wanted to inspect the syscall's return
|
||||||
// value, and call EventSyscallReturn for the parent process if so.
|
// value, and call EventSyscallReturn for the parent process if so.
|
||||||
|
@ -1087,7 +1152,7 @@ void Monitor::EventPtraceNewProcess(pid_t pid, int event_msg) {
|
||||||
ContinueProcess(pid, 0);
|
ContinueProcess(pid, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::EventPtraceExec(pid_t pid, int event_msg) {
|
void PtraceMonitor::EventPtraceExec(pid_t pid, int event_msg) {
|
||||||
if (!IsActivelyMonitoring()) {
|
if (!IsActivelyMonitoring()) {
|
||||||
VLOG(1) << "PTRACE_EVENT_EXEC seen from PID: " << event_msg
|
VLOG(1) << "PTRACE_EVENT_EXEC seen from PID: " << event_msg
|
||||||
<< ". SANDBOX ENABLED!";
|
<< ". SANDBOX ENABLED!";
|
||||||
|
@ -1112,14 +1177,14 @@ void Monitor::EventPtraceExec(pid_t pid, int event_msg) {
|
||||||
ContinueProcess(pid, 0);
|
ContinueProcess(pid, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::EventPtraceExit(pid_t pid, int event_msg) {
|
void PtraceMonitor::EventPtraceExit(pid_t pid, int event_msg) {
|
||||||
// Forget about any syscalls in progress for this PID.
|
// Forget about any syscalls in progress for this PID.
|
||||||
syscalls_in_progress_.erase(pid);
|
syscalls_in_progress_.erase(pid);
|
||||||
|
|
||||||
// A regular exit, let it continue (fast-path).
|
// A regular exit, let it continue (fast-path).
|
||||||
if (ABSL_PREDICT_TRUE(
|
if (ABSL_PREDICT_TRUE(WIFEXITED(event_msg) &&
|
||||||
WIFEXITED(event_msg) &&
|
(!policy_->collect_stacktrace_on_exit_ ||
|
||||||
(!policy_->collect_stacktrace_on_exit_ || pid != pid_))) {
|
pid != process_.main_pid))) {
|
||||||
ContinueProcess(pid, 0);
|
ContinueProcess(pid, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1130,7 +1195,7 @@ void Monitor::EventPtraceExit(pid_t pid, int event_msg) {
|
||||||
absl::GetFlag(FLAGS_sandbox2_log_all_stack_traces);
|
absl::GetFlag(FLAGS_sandbox2_log_all_stack_traces);
|
||||||
// Fetch the registers as we'll need them to fill the result in any case
|
// Fetch the registers as we'll need them to fill the result in any case
|
||||||
auto regs = std::make_unique<Regs>(pid);
|
auto regs = std::make_unique<Regs>(pid);
|
||||||
if (is_seccomp || pid == pid_ || log_stack_trace) {
|
if (is_seccomp || pid == process_.main_pid || log_stack_trace) {
|
||||||
auto status = regs->Fetch();
|
auto status = regs->Fetch();
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
LOG(ERROR) << "failed to fetch regs: " << status;
|
LOG(ERROR) << "failed to fetch regs: " << status;
|
||||||
|
@ -1152,7 +1217,7 @@ void Monitor::EventPtraceExit(pid_t pid, int event_msg) {
|
||||||
// 2) Process was killed because it hit a timeout.
|
// 2) Process was killed because it hit a timeout.
|
||||||
// 3) Regular signal/other exit cause.
|
// 3) Regular signal/other exit cause.
|
||||||
// 4) Normal exit for which we want to obtain stack trace.
|
// 4) Normal exit for which we want to obtain stack trace.
|
||||||
if (pid == pid_) {
|
if (pid == process_.main_pid) {
|
||||||
VLOG(1) << "PID: " << pid << " main special exit";
|
VLOG(1) << "PID: " << pid << " main special exit";
|
||||||
if (network_violation_) {
|
if (network_violation_) {
|
||||||
SetExitStatusCode(Result::VIOLATION, Result::VIOLATION_NETWORK);
|
SetExitStatusCode(Result::VIOLATION, Result::VIOLATION_NETWORK);
|
||||||
|
@ -1166,6 +1231,7 @@ void Monitor::EventPtraceExit(pid_t pid, int event_msg) {
|
||||||
} else {
|
} else {
|
||||||
SetExitStatusCode(Result::SIGNALED, WTERMSIG(event_msg));
|
SetExitStatusCode(Result::SIGNALED, WTERMSIG(event_msg));
|
||||||
}
|
}
|
||||||
|
LOG(INFO) << "CPU limit: " << executor_->limits()->rlimit_cpu().rlim_cur;
|
||||||
SetAdditionalResultInfo(std::move(regs));
|
SetAdditionalResultInfo(std::move(regs));
|
||||||
} else if (log_stack_trace) {
|
} else if (log_stack_trace) {
|
||||||
// In case pid == pid_ the stack trace will be logged anyway. So we need
|
// In case pid == pid_ the stack trace will be logged anyway. So we need
|
||||||
|
@ -1182,7 +1248,7 @@ void Monitor::EventPtraceExit(pid_t pid, int event_msg) {
|
||||||
ContinueProcess(pid, 0);
|
ContinueProcess(pid, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::EventPtraceStop(pid_t pid, int stopsig) {
|
void PtraceMonitor::EventPtraceStop(pid_t pid, int stopsig) {
|
||||||
// It's not a real stop signal. For example PTRACE_O_TRACECLONE and similar
|
// It's not a real stop signal. For example PTRACE_O_TRACECLONE and similar
|
||||||
// flags to ptrace(PTRACE_SEIZE) might generate this event with SIGTRAP.
|
// flags to ptrace(PTRACE_SEIZE) might generate this event with SIGTRAP.
|
||||||
if (stopsig != SIGSTOP && stopsig != SIGTSTP && stopsig != SIGTTIN &&
|
if (stopsig != SIGSTOP && stopsig != SIGTSTP && stopsig != SIGTTIN &&
|
||||||
|
@ -1196,7 +1262,7 @@ void Monitor::EventPtraceStop(pid_t pid, int stopsig) {
|
||||||
StopProcess(pid, 0);
|
StopProcess(pid, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::StateProcessStopped(pid_t pid, int status) {
|
void PtraceMonitor::StateProcessStopped(pid_t pid, int status) {
|
||||||
int stopsig = WSTOPSIG(status);
|
int stopsig = WSTOPSIG(status);
|
||||||
// We use PTRACE_O_TRACESYSGOOD, so we can tell it's a syscall stop without
|
// We use PTRACE_O_TRACESYSGOOD, so we can tell it's a syscall stop without
|
||||||
// calling PTRACE_GETSIGINFO by checking the value of the reported signal.
|
// calling PTRACE_GETSIGINFO by checking the value of the reported signal.
|
||||||
|
@ -1223,7 +1289,7 @@ void Monitor::StateProcessStopped(pid_t pid, int status) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ABSL_PREDICT_FALSE(pid == pid_ && should_dump_stack_ &&
|
if (ABSL_PREDICT_FALSE(pid == process_.main_pid && should_dump_stack_ &&
|
||||||
executor_->libunwind_sbox_for_pid_ == 0 &&
|
executor_->libunwind_sbox_for_pid_ == 0 &&
|
||||||
policy_->GetNamespace())) {
|
policy_->GetNamespace())) {
|
||||||
auto stack_trace = [this,
|
auto stack_trace = [this,
|
||||||
|
@ -1295,7 +1361,7 @@ void Monitor::StateProcessStopped(pid_t pid, int status) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::LogSyscallViolationExplanation(const Syscall& syscall) const {
|
void MonitorBase::LogSyscallViolationExplanation(const Syscall& syscall) const {
|
||||||
const uintptr_t syscall_nr = syscall.nr();
|
const uintptr_t syscall_nr = syscall.nr();
|
||||||
const uintptr_t arg0 = syscall.args()[0];
|
const uintptr_t arg0 = syscall.args()[0];
|
||||||
|
|
||||||
|
@ -1325,7 +1391,7 @@ void Monitor::LogSyscallViolationExplanation(const Syscall& syscall) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::EnableNetworkProxyServer() {
|
void MonitorBase::EnableNetworkProxyServer() {
|
||||||
int fd = ipc_->ReceiveFd(NetworkProxyClient::kFDName);
|
int fd = ipc_->ReceiveFd(NetworkProxyClient::kFDName);
|
||||||
|
|
||||||
network_proxy_server_ = std::make_unique<NetworkProxyServer>(
|
network_proxy_server_ = std::make_unique<NetworkProxyServer>(
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "absl/container/flat_hash_map.h"
|
#include "absl/container/flat_hash_map.h"
|
||||||
|
#include "absl/status/statusor.h"
|
||||||
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "absl/synchronization/notification.h"
|
#include "absl/synchronization/notification.h"
|
||||||
#include "sandboxed_api/sandbox2/comms.h"
|
#include "sandboxed_api/sandbox2/comms.h"
|
||||||
#include "sandboxed_api/sandbox2/executor.h"
|
#include "sandboxed_api/sandbox2/executor.h"
|
||||||
|
@ -39,36 +41,66 @@
|
||||||
#include "sandboxed_api/sandbox2/regs.h"
|
#include "sandboxed_api/sandbox2/regs.h"
|
||||||
#include "sandboxed_api/sandbox2/result.h"
|
#include "sandboxed_api/sandbox2/result.h"
|
||||||
#include "sandboxed_api/sandbox2/syscall.h"
|
#include "sandboxed_api/sandbox2/syscall.h"
|
||||||
|
#include "sandboxed_api/util/raw_logging.h"
|
||||||
|
|
||||||
namespace sandbox2 {
|
namespace sandbox2 {
|
||||||
|
|
||||||
class Monitor final {
|
class MonitorBase {
|
||||||
public:
|
public:
|
||||||
// executor, policy and notify are not owned by the Monitor
|
// executor, policy and notify are not owned by the Monitor
|
||||||
Monitor(Executor* executor, Policy* policy, Notify* notify);
|
MonitorBase(Executor* executor, Policy* policy, Notify* notify);
|
||||||
|
|
||||||
Monitor(const Monitor&) = delete;
|
MonitorBase(const MonitorBase&) = delete;
|
||||||
Monitor& operator=(const Monitor&) = delete;
|
MonitorBase& operator=(const MonitorBase&) = delete;
|
||||||
|
|
||||||
~Monitor();
|
virtual ~MonitorBase();
|
||||||
|
|
||||||
private:
|
|
||||||
friend class Sandbox2;
|
|
||||||
|
|
||||||
// Timeout used with sigtimedwait (0.5s).
|
|
||||||
static constexpr int kWakeUpPeriodSec = 0L;
|
|
||||||
static constexpr int kWakeUpPeriodNSec = (500L * 1000L * 1000L);
|
|
||||||
|
|
||||||
// Starts the Monitor.
|
// Starts the Monitor.
|
||||||
void Run();
|
void Launch();
|
||||||
|
|
||||||
// Getters for private fields.
|
// Getters for private fields.
|
||||||
bool IsDone() const { return done_notification_.HasBeenNotified(); }
|
bool IsDone() const { return done_notification_.HasBeenNotified(); }
|
||||||
|
|
||||||
// Getter/Setter for wait_for_execve_.
|
// Enable network proxy server, this will start a thread in the sandbox
|
||||||
bool IsActivelyMonitoring();
|
// that waits for connection requests from the sandboxee.
|
||||||
void SetActivelyMonitoring();
|
void EnableNetworkProxyServer();
|
||||||
|
|
||||||
|
pid_t pid() const { return process_.main_pid; }
|
||||||
|
|
||||||
|
const Result& result() const { return result_; }
|
||||||
|
|
||||||
|
absl::StatusOr<Result> AwaitResultWithTimeout(absl::Duration timeout);
|
||||||
|
|
||||||
|
virtual void Kill() = 0;
|
||||||
|
virtual void DumpStackTrace() = 0;
|
||||||
|
virtual void SetWallTimeLimit(absl::Duration limit) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void OnDone();
|
||||||
|
// Sets basic info status and reason code in the result object.
|
||||||
|
void SetExitStatusCode(Result::StatusEnum final_status,
|
||||||
|
uintptr_t reason_code);
|
||||||
|
// Logs a SANDBOX VIOLATION message based on the registers and additional
|
||||||
|
// explanation for the reason of the violation.
|
||||||
|
void LogSyscallViolation(const Syscall& syscall) const;
|
||||||
|
|
||||||
|
// Internal objects, owned by the Sandbox2 object.
|
||||||
|
Executor* executor_;
|
||||||
|
Notify* notify_;
|
||||||
|
Policy* policy_;
|
||||||
|
// The sandboxee process.
|
||||||
|
Executor::Process process_;
|
||||||
|
Result result_;
|
||||||
|
// Comms channel ptr, copied from the Executor object for convenience.
|
||||||
|
Comms* comms_;
|
||||||
|
// Log file specified by
|
||||||
|
// --sandbox_danger_danger_permit_all_and_log flag.
|
||||||
|
FILE* log_file_ = nullptr;
|
||||||
|
// Handle to the class responsible for proxying and validating connect()
|
||||||
|
// requests.
|
||||||
|
std::unique_ptr<NetworkProxyServer> network_proxy_server_;
|
||||||
|
|
||||||
|
private:
|
||||||
// Sends Policy to the Client.
|
// Sends Policy to the Client.
|
||||||
// Returns success/failure status.
|
// Returns success/failure status.
|
||||||
bool InitSendPolicy();
|
bool InitSendPolicy();
|
||||||
|
@ -77,13 +109,6 @@ class Monitor final {
|
||||||
// Returns success/failure status.
|
// Returns success/failure status.
|
||||||
bool WaitForSandboxReady();
|
bool WaitForSandboxReady();
|
||||||
|
|
||||||
// ptrace(PTRACE_SEIZE) to the Client.
|
|
||||||
// Returns success/failure status.
|
|
||||||
bool InitPtraceAttach();
|
|
||||||
|
|
||||||
// Sets up required signal masks/handlers; prepare mask for sigtimedwait().
|
|
||||||
bool InitSetupSignals(sigset_t* sset);
|
|
||||||
|
|
||||||
// Sends information about data exchange channels.
|
// Sends information about data exchange channels.
|
||||||
bool InitSendIPC();
|
bool InitSendIPC();
|
||||||
|
|
||||||
|
@ -96,48 +121,95 @@ class Monitor final {
|
||||||
// Applies individual limit on the sandboxee.
|
// Applies individual limit on the sandboxee.
|
||||||
bool InitApplyLimit(pid_t pid, int resource, const rlimit64& rlim) const;
|
bool InitApplyLimit(pid_t pid, int resource, const rlimit64& rlim) const;
|
||||||
|
|
||||||
// Kills the main traced PID with PTRACE_KILL.
|
// Logs an additional explanation for the possible reason of the violation
|
||||||
// Returns false if an error occured and process could not be killed.
|
// based on the registers.
|
||||||
bool KillSandboxee();
|
void LogSyscallViolationExplanation(const Syscall& syscall) const;
|
||||||
|
|
||||||
// Interrupts the main traced PID with PTRACE_INTERRUPT.
|
virtual void RunInternal() = 0;
|
||||||
// Returns false if an error occured and process could not be interrupted.
|
virtual void Join() = 0;
|
||||||
bool InterruptSandboxee();
|
|
||||||
|
// IPC ptr, used for exchanging data with the sandboxee.
|
||||||
|
IPC* ipc_;
|
||||||
|
|
||||||
|
// The field indicates whether the sandboxing task has been completed (either
|
||||||
|
// successfully or with error).
|
||||||
|
absl::Notification done_notification_;
|
||||||
|
|
||||||
|
// Empty temp file used for mapping the comms fd when the Tomoyo LSM is
|
||||||
|
// active.
|
||||||
|
std::string comms_fd_dev_;
|
||||||
|
|
||||||
|
std::thread network_proxy_thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PtraceMonitor : public MonitorBase {
|
||||||
|
public:
|
||||||
|
PtraceMonitor(Executor* executor, Policy* policy, Notify* notify);
|
||||||
|
~PtraceMonitor() { Join(); }
|
||||||
|
|
||||||
|
void Kill() override {
|
||||||
|
external_kill_request_flag_.clear(std::memory_order_relaxed);
|
||||||
|
NotifyMonitor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpStackTrace() override {
|
||||||
|
dump_stack_request_flag_.clear(std::memory_order_relaxed);
|
||||||
|
NotifyMonitor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetWallTimeLimit(absl::Duration limit) override {
|
||||||
|
if (limit == absl::ZeroDuration()) {
|
||||||
|
VLOG(1) << "Disarming walltime timer to ";
|
||||||
|
deadline_millis_.store(0, std::memory_order_relaxed);
|
||||||
|
} else {
|
||||||
|
VLOG(1) << "Will set the walltime timer to " << limit;
|
||||||
|
absl::Time deadline = absl::Now() + limit;
|
||||||
|
deadline_millis_.store(absl::ToUnixMillis(deadline),
|
||||||
|
std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Timeout used with sigtimedwait (0.5s).
|
||||||
|
static constexpr int kWakeUpPeriodSec = 0L;
|
||||||
|
static constexpr int kWakeUpPeriodNSec = (500L * 1000L * 1000L);
|
||||||
|
|
||||||
// Waits for events from monitored clients and signals from the main process.
|
// Waits for events from monitored clients and signals from the main process.
|
||||||
void MainLoop(sigset_t* sset);
|
void RunInternal() override;
|
||||||
|
void Join() override;
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
// Notifies monitor about a state change
|
||||||
|
void NotifyMonitor();
|
||||||
|
|
||||||
|
// PID called a traced syscall, or was killed due to syscall.
|
||||||
|
void ActionProcessSyscall(Regs* regs, const Syscall& syscall);
|
||||||
|
|
||||||
|
// Getter/Setter for wait_for_execve_.
|
||||||
|
bool IsActivelyMonitoring();
|
||||||
|
void SetActivelyMonitoring();
|
||||||
|
|
||||||
// Process with given PID changed state to a stopped state.
|
// Process with given PID changed state to a stopped state.
|
||||||
void StateProcessStopped(pid_t pid, int status);
|
void StateProcessStopped(pid_t pid, int status);
|
||||||
|
|
||||||
|
// Tells if collecting stack trace is at all possible.
|
||||||
|
bool StackTraceCollectionPossible() const;
|
||||||
|
|
||||||
|
// Whether a stack trace should be collected given the current status
|
||||||
|
bool ShouldCollectStackTrace() const;
|
||||||
|
|
||||||
|
// Sets additional information in the result object, such as program name,
|
||||||
|
// stack trace etc.
|
||||||
|
void SetAdditionalResultInfo(std::unique_ptr<Regs> regs);
|
||||||
|
|
||||||
// Logs the syscall violation and kills the process afterwards.
|
// Logs the syscall violation and kills the process afterwards.
|
||||||
void ActionProcessSyscallViolation(Regs* regs, const Syscall& syscall,
|
void ActionProcessSyscallViolation(Regs* regs, const Syscall& syscall,
|
||||||
ViolationType violation_type);
|
ViolationType violation_type);
|
||||||
|
|
||||||
// PID called a traced syscall, or was killed due to syscall.
|
|
||||||
void ActionProcessSyscall(Regs* regs, const Syscall& syscall);
|
|
||||||
|
|
||||||
// Sets basic info status and reason code in the result object.
|
|
||||||
void SetExitStatusCode(Result::StatusEnum final_status,
|
|
||||||
uintptr_t reason_code);
|
|
||||||
// Tells if collecting stack trace is at all possible.
|
|
||||||
bool StackTraceCollectionPossible();
|
|
||||||
// Whether a stack trace should be collected given the status
|
|
||||||
bool ShouldCollectStackTrace();
|
|
||||||
// Sets additional information in the result object, such as program name,
|
|
||||||
// stack trace etc.
|
|
||||||
void SetAdditionalResultInfo(std::unique_ptr<Regs> regs);
|
|
||||||
// Gets and logs stack trace.
|
// Gets and logs stack trace.
|
||||||
absl::StatusOr<std::vector<std::string>> GetAndLogStackTrace(
|
absl::StatusOr<std::vector<std::string>> GetAndLogStackTrace(
|
||||||
const Regs* regs);
|
const Regs* regs);
|
||||||
|
|
||||||
// Logs a SANDBOX VIOLATION message based on the registers and additional
|
|
||||||
// explanation for the reason of the violation.
|
|
||||||
void LogSyscallViolation(const Syscall& syscall) const;
|
|
||||||
// Logs an additional explanation for the possible reason of the violation
|
|
||||||
// based on the registers.
|
|
||||||
void LogSyscallViolationExplanation(const Syscall& syscall) const;
|
|
||||||
|
|
||||||
void LogStackTraceOfPid(pid_t pid);
|
void LogStackTraceOfPid(pid_t pid);
|
||||||
|
|
||||||
// Ptrace events:
|
// Ptrace events:
|
||||||
|
@ -159,37 +231,31 @@ class Monitor final {
|
||||||
// Processes syscall exit.
|
// Processes syscall exit.
|
||||||
void EventSyscallExit(pid_t pid);
|
void EventSyscallExit(pid_t pid);
|
||||||
|
|
||||||
// Enable network proxy server, this will start a thread in the sandbox
|
// Kills the main traced PID with PTRACE_KILL.
|
||||||
// that waits for connection requests from the sandboxee.
|
// Returns false if an error occured and process could not be killed.
|
||||||
void EnableNetworkProxyServer();
|
bool KillSandboxee();
|
||||||
|
|
||||||
// Internal objects, owned by the Sandbox2 object.
|
// Interrupts the main traced PID with PTRACE_INTERRUPT.
|
||||||
Executor* executor_;
|
// Returns false if an error occured and process could not be interrupted.
|
||||||
Notify* notify_;
|
bool InterruptSandboxee();
|
||||||
Policy* policy_;
|
|
||||||
Result result_;
|
// Sets up required signal masks/handlers; prepare mask for sigtimedwait().
|
||||||
// Comms channel ptr, copied from the Executor object for convenience.
|
bool InitSetupSignals();
|
||||||
Comms* comms_;
|
|
||||||
// IPC ptr, used for exchanging data with the sandboxee.
|
// ptrace(PTRACE_SEIZE) to the Client.
|
||||||
IPC* ipc_;
|
// Returns success/failure status.
|
||||||
|
bool InitPtraceAttach();
|
||||||
|
|
||||||
// Parent (the Sandbox2 object) waits on it, until we either enable
|
// Parent (the Sandbox2 object) waits on it, until we either enable
|
||||||
// monitoring of a process (sandboxee) successfully, or the setup process
|
// monitoring of a process (sandboxee) successfully, or the setup process
|
||||||
// fails.
|
// fails.
|
||||||
absl::Notification setup_notification_;
|
absl::Notification setup_notification_;
|
||||||
// The field indicates whether the sandboxing task has been completed (either
|
// Deadline in Unix millis
|
||||||
// successfully or with error).
|
std::atomic<int64_t> deadline_millis_{0};
|
||||||
absl::Notification done_notification_;
|
|
||||||
|
|
||||||
// The main tracked PID.
|
|
||||||
pid_t pid_ = -1;
|
|
||||||
|
|
||||||
// False iff external kill is requested
|
// False iff external kill is requested
|
||||||
std::atomic_flag external_kill_request_flag_ = ATOMIC_FLAG_INIT;
|
std::atomic_flag external_kill_request_flag_ = ATOMIC_FLAG_INIT;
|
||||||
// False iff dump stack is requested
|
// False iff dump stack is requested
|
||||||
std::atomic_flag dump_stack_request_flag_ = ATOMIC_FLAG_INIT;
|
std::atomic_flag dump_stack_request_flag_ = ATOMIC_FLAG_INIT;
|
||||||
// Deadline in Unix millis
|
|
||||||
std::atomic<int64_t> deadline_millis_{0};
|
|
||||||
// Was external kill sent to the sandboxee
|
// Was external kill sent to the sandboxee
|
||||||
bool external_kill_ = false;
|
bool external_kill_ = false;
|
||||||
// Network violation occurred and process of killing sandboxee started
|
// Network violation occurred and process of killing sandboxee started
|
||||||
|
@ -198,27 +264,19 @@ class Monitor final {
|
||||||
bool timed_out_ = false;
|
bool timed_out_ = false;
|
||||||
// Should we dump the main sandboxed PID's stack?
|
// Should we dump the main sandboxed PID's stack?
|
||||||
bool should_dump_stack_ = false;
|
bool should_dump_stack_ = false;
|
||||||
|
|
||||||
// Is the sandboxee actively monitored, or maybe we're waiting for execve()?
|
// Is the sandboxee actively monitored, or maybe we're waiting for execve()?
|
||||||
bool wait_for_execve_;
|
bool wait_for_execve_;
|
||||||
// Is the sandboxee forked from a custom forkserver?
|
|
||||||
bool uses_custom_forkserver_;
|
|
||||||
// Log file specified by
|
|
||||||
// --sandbox_danger_danger_permit_all_and_log flag.
|
|
||||||
FILE* log_file_ = nullptr;
|
|
||||||
|
|
||||||
// Empty temp file used for mapping the comms fd when the Tomoyo LSM is
|
|
||||||
// active.
|
|
||||||
std::string comms_fd_dev_;
|
|
||||||
|
|
||||||
// Handle to the class responsible for proxying and validating connect()
|
|
||||||
// requests.
|
|
||||||
std::unique_ptr<NetworkProxyServer> network_proxy_server_;
|
|
||||||
|
|
||||||
std::thread network_proxy_thread_;
|
|
||||||
|
|
||||||
// Syscalls that are running, whose result values we want to inspect.
|
// Syscalls that are running, whose result values we want to inspect.
|
||||||
absl::flat_hash_map<pid_t, Syscall> syscalls_in_progress_;
|
absl::flat_hash_map<pid_t, Syscall> syscalls_in_progress_;
|
||||||
|
sigset_t sset_;
|
||||||
|
// Is the sandboxee forked from a custom forkserver?
|
||||||
|
bool uses_custom_forkserver_;
|
||||||
|
|
||||||
|
// Monitor thread object.
|
||||||
|
std::unique_ptr<std::thread> thread_;
|
||||||
|
|
||||||
|
// Synchronizes monitor thread deletion and notifying the monitor.
|
||||||
|
absl::Mutex notify_mutex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sandbox2
|
} // namespace sandbox2
|
||||||
|
|
|
@ -59,7 +59,8 @@ class Policy final {
|
||||||
void GetPolicyDescription(PolicyDescription* policy) const;
|
void GetPolicyDescription(PolicyDescription* policy) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Monitor;
|
friend class MonitorBase;
|
||||||
|
friend class PtraceMonitor;
|
||||||
friend class PolicyBuilder;
|
friend class PolicyBuilder;
|
||||||
friend class PolicyBuilderPeer; // For testing
|
friend class PolicyBuilderPeer; // For testing
|
||||||
friend class StackTracePeer;
|
friend class StackTracePeer;
|
||||||
|
|
|
@ -22,45 +22,16 @@
|
||||||
|
|
||||||
#include "absl/log/check.h"
|
#include "absl/log/check.h"
|
||||||
#include "absl/status/statusor.h"
|
#include "absl/status/statusor.h"
|
||||||
#include "absl/synchronization/mutex.h"
|
|
||||||
#include "absl/time/time.h"
|
#include "absl/time/time.h"
|
||||||
#include "sandboxed_api/sandbox2/monitor.h"
|
#include "sandboxed_api/sandbox2/monitor.h"
|
||||||
#include "sandboxed_api/sandbox2/result.h"
|
#include "sandboxed_api/sandbox2/result.h"
|
||||||
#include "sandboxed_api/util/raw_logging.h"
|
|
||||||
|
|
||||||
namespace sandbox2 {
|
namespace sandbox2 {
|
||||||
|
|
||||||
Sandbox2::~Sandbox2() {
|
|
||||||
if (monitor_thread_ && monitor_thread_->joinable()) {
|
|
||||||
monitor_thread_->join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::StatusOr<Result> Sandbox2::AwaitResultWithTimeout(
|
absl::StatusOr<Result> Sandbox2::AwaitResultWithTimeout(
|
||||||
absl::Duration timeout) {
|
absl::Duration timeout) {
|
||||||
CHECK(monitor_ != nullptr) << "Sandbox was not launched yet";
|
CHECK(monitor_ != nullptr) << "Sandbox was not launched yet";
|
||||||
CHECK(monitor_thread_ != nullptr) << "Sandbox was already waited on";
|
return monitor_->AwaitResultWithTimeout(timeout);
|
||||||
|
|
||||||
auto done =
|
|
||||||
monitor_->done_notification_.WaitForNotificationWithTimeout(timeout);
|
|
||||||
if (!done) {
|
|
||||||
return absl::DeadlineExceededError("Sandbox did not finish within timeout");
|
|
||||||
}
|
|
||||||
{
|
|
||||||
absl::MutexLock lock(&monitor_notify_mutex_);
|
|
||||||
monitor_thread_->join();
|
|
||||||
|
|
||||||
CHECK(IsTerminated()) << "Monitor did not terminate";
|
|
||||||
|
|
||||||
// Reset the Monitor Thread object to its initial state, as to mark that
|
|
||||||
// this object cannot be used anymore to control behavior of the sandboxee
|
|
||||||
// (e.g. via signals).
|
|
||||||
monitor_thread_.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
VLOG(1) << "Final execution status: " << monitor_->result_.ToString();
|
|
||||||
CHECK(monitor_->result_.final_status() != Result::UNSET);
|
|
||||||
return std::move(monitor_->result_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Sandbox2::AwaitResult() {
|
Result Sandbox2::AwaitResult() {
|
||||||
|
@ -72,61 +43,36 @@ bool Sandbox2::RunAsync() {
|
||||||
|
|
||||||
// If the sandboxee setup failed we return 'false' here.
|
// If the sandboxee setup failed we return 'false' here.
|
||||||
if (monitor_->IsDone() &&
|
if (monitor_->IsDone() &&
|
||||||
monitor_->result_.final_status() == Result::SETUP_ERROR) {
|
monitor_->result().final_status() == Result::SETUP_ERROR) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sandbox2::NotifyMonitor() {
|
|
||||||
absl::ReaderMutexLock lock(&monitor_notify_mutex_);
|
|
||||||
if (monitor_thread_ != nullptr) {
|
|
||||||
pthread_kill(monitor_thread_->native_handle(), SIGCHLD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sandbox2::Kill() {
|
void Sandbox2::Kill() {
|
||||||
CHECK(monitor_ != nullptr) << "Sandbox was not launched yet";
|
CHECK(monitor_ != nullptr) << "Sandbox was not launched yet";
|
||||||
|
monitor_->Kill();
|
||||||
monitor_->external_kill_request_flag_.clear(std::memory_order_relaxed);
|
|
||||||
NotifyMonitor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sandbox2::DumpStackTrace() {
|
void Sandbox2::DumpStackTrace() {
|
||||||
CHECK(monitor_ != nullptr) << "Sandbox was not launched yet";
|
CHECK(monitor_ != nullptr) << "Sandbox was not launched yet";
|
||||||
|
monitor_->DumpStackTrace();
|
||||||
monitor_->dump_stack_request_flag_.clear(std::memory_order_relaxed);
|
|
||||||
NotifyMonitor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Sandbox2::IsTerminated() const {
|
bool Sandbox2::IsTerminated() const {
|
||||||
CHECK(monitor_ != nullptr) << "Sandbox was not launched yet";
|
CHECK(monitor_ != nullptr) << "Sandbox was not launched yet";
|
||||||
|
|
||||||
return monitor_->IsDone();
|
return monitor_->IsDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sandbox2::set_walltime_limit(absl::Duration limit) const {
|
void Sandbox2::set_walltime_limit(absl::Duration limit) const {
|
||||||
if (limit == absl::ZeroDuration()) {
|
CHECK(monitor_ != nullptr) << "Sandbox was not launched yet";
|
||||||
VLOG(1) << "Disarming walltime timer to ";
|
monitor_->SetWallTimeLimit(limit);
|
||||||
monitor_->deadline_millis_.store(0, std::memory_order_relaxed);
|
|
||||||
} else {
|
|
||||||
VLOG(1) << "Will set the walltime timer to " << limit;
|
|
||||||
absl::Time deadline = absl::Now() + limit;
|
|
||||||
monitor_->deadline_millis_.store(absl::ToUnixMillis(deadline),
|
|
||||||
std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sandbox2::Launch() {
|
void Sandbox2::Launch() {
|
||||||
monitor_ =
|
monitor_ = std::make_unique<PtraceMonitor>(executor_.get(), policy_.get(),
|
||||||
std::make_unique<Monitor>(executor_.get(), policy_.get(), notify_.get());
|
notify_.get());
|
||||||
monitor_thread_ =
|
monitor_->Launch();
|
||||||
std::make_unique<std::thread>(&Monitor::Run, monitor_.get());
|
|
||||||
|
|
||||||
// Wait for the Monitor to set-up the sandboxee correctly (or fail while
|
|
||||||
// doing that). From here on, it is safe to use the IPC object for
|
|
||||||
// non-sandbox-related data exchange.
|
|
||||||
monitor_->setup_notification_.WaitForNotification();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace sandbox2
|
} // namespace sandbox2
|
||||||
|
|
|
@ -20,13 +20,10 @@
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread> // NOLINT(build/c++11)
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "absl/base/macros.h"
|
#include "absl/base/macros.h"
|
||||||
#include "absl/log/log.h"
|
|
||||||
#include "absl/status/statusor.h"
|
#include "absl/status/statusor.h"
|
||||||
#include "absl/synchronization/mutex.h"
|
|
||||||
#include "sandboxed_api/sandbox2/comms.h"
|
#include "sandboxed_api/sandbox2/comms.h"
|
||||||
#include "sandboxed_api/sandbox2/executor.h"
|
#include "sandboxed_api/sandbox2/executor.h"
|
||||||
#include "sandboxed_api/sandbox2/ipc.h"
|
#include "sandboxed_api/sandbox2/ipc.h"
|
||||||
|
@ -54,8 +51,6 @@ class Sandbox2 final {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~Sandbox2();
|
|
||||||
|
|
||||||
Sandbox2(const Sandbox2&) = delete;
|
Sandbox2(const Sandbox2&) = delete;
|
||||||
Sandbox2& operator=(const Sandbox2&) = delete;
|
Sandbox2& operator=(const Sandbox2&) = delete;
|
||||||
|
|
||||||
|
@ -106,7 +101,7 @@ class Sandbox2 final {
|
||||||
void set_walltime_limit(absl::Duration limit) const;
|
void set_walltime_limit(absl::Duration limit) const;
|
||||||
|
|
||||||
// Returns the process id inside the executor.
|
// Returns the process id inside the executor.
|
||||||
pid_t pid() const { return monitor_ != nullptr ? monitor_->pid_ : -1; }
|
pid_t pid() const { return monitor_ != nullptr ? monitor_->pid() : -1; }
|
||||||
|
|
||||||
// Gets the comms inside the executor.
|
// Gets the comms inside the executor.
|
||||||
Comms* comms() {
|
Comms* comms() {
|
||||||
|
@ -116,8 +111,6 @@ class Sandbox2 final {
|
||||||
private:
|
private:
|
||||||
// Launches the Monitor.
|
// Launches the Monitor.
|
||||||
void Launch();
|
void Launch();
|
||||||
// Notifies monitor about a state change
|
|
||||||
void NotifyMonitor();
|
|
||||||
|
|
||||||
// Executor set by user - owned by Sandbox2.
|
// Executor set by user - owned by Sandbox2.
|
||||||
std::unique_ptr<Executor> executor_;
|
std::unique_ptr<Executor> executor_;
|
||||||
|
@ -129,13 +122,7 @@ class Sandbox2 final {
|
||||||
std::unique_ptr<Notify> notify_;
|
std::unique_ptr<Notify> notify_;
|
||||||
|
|
||||||
// Monitor object - owned by Sandbox2.
|
// Monitor object - owned by Sandbox2.
|
||||||
std::unique_ptr<Monitor> monitor_;
|
std::unique_ptr<MonitorBase> monitor_;
|
||||||
|
|
||||||
// Monitor thread object - owned by Sandbox2.
|
|
||||||
std::unique_ptr<std::thread> monitor_thread_;
|
|
||||||
|
|
||||||
// Synchronizes monitor thread deletion and notifying the monitor.
|
|
||||||
absl::Mutex monitor_notify_mutex_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sandbox2
|
} // namespace sandbox2
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread> // NOLINT(build/c++11)
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
|
@ -180,6 +181,24 @@ TEST(RunAsyncTest, SandboxeeTimeoutDisabledStacktraces) {
|
||||||
EXPECT_THAT(result.GetStackTrace(), IsEmpty());
|
EXPECT_THAT(result.GetStackTrace(), IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(RunAsyncTest, SandboxeeNotKilledWhenStartingThreadFinishes) {
|
||||||
|
const std::string path = GetTestSourcePath("sandbox2/testcases/minimal");
|
||||||
|
std::vector<std::string> args = {path};
|
||||||
|
auto executor = std::make_unique<Executor>(path, args);
|
||||||
|
|
||||||
|
SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
|
||||||
|
PolicyBuilder()
|
||||||
|
// Don't restrict the syscalls at all.
|
||||||
|
.DangerDefaultAllowAll()
|
||||||
|
.CollectStacktracesOnExit(true)
|
||||||
|
.TryBuild());
|
||||||
|
Sandbox2 sandbox(std::move(executor), std::move(policy));
|
||||||
|
std::thread sandbox_start_thread([&sandbox]() { sandbox.RunAsync(); });
|
||||||
|
sandbox_start_thread.join();
|
||||||
|
Result result = sandbox.AwaitResult();
|
||||||
|
EXPECT_EQ(result.final_status(), Result::OK);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(StarvationTest, MonitorIsNotStarvedByTheSandboxee) {
|
TEST(StarvationTest, MonitorIsNotStarvedByTheSandboxee) {
|
||||||
const std::string path = GetTestSourcePath("sandbox2/testcases/starve");
|
const std::string path = GetTestSourcePath("sandbox2/testcases/starve");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user