Skip to content

Commit 9104d71

Browse files
happyCoder92copybara-github
authored andcommitted
UnotifyMonitor: allow only initial execveat
PiperOrigin-RevId: 734107977 Change-Id: I492002953c23021155e8b9596c828fb77888a42a
1 parent 2b52e66 commit 9104d71

11 files changed

+63
-22
lines changed

sandboxed_api/sandbox2/BUILD

+2
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ cc_library(
203203
"@com_google_sandboxed_api//sandboxed_api:config",
204204
"@com_google_sandboxed_api//sandboxed_api/sandbox2/network_proxy:filtering",
205205
"@com_google_sandboxed_api//sandboxed_api/sandbox2/util:bpf_helper",
206+
"@com_google_sandboxed_api//sandboxed_api/sandbox2/util:seccomp_unotify",
206207
],
207208
)
208209

@@ -469,6 +470,7 @@ cc_library(
469470
":notify",
470471
":policy",
471472
":result",
473+
":util",
472474
"@com_google_absl//absl/base:core_headers",
473475
"@com_google_absl//absl/cleanup",
474476
"@com_google_absl//absl/log",

sandboxed_api/sandbox2/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ target_link_libraries(sandbox2_policy
170170
sandbox2::bpf_helper
171171
sandbox2::bpfdisassembler
172172
sandbox2::regs
173+
sandbox2::seccomp_unotify
173174
sandbox2::syscall
174175
sapi::base
175176
sapi::config
@@ -488,6 +489,7 @@ target_link_libraries(sandbox2_monitor_unotify
488489
sandbox2::client
489490
sandbox2::forkserver_proto
490491
sandbox2::seccomp_unotify
492+
sandbox2::util
491493
sapi::status
492494
PUBLIC sandbox2::executor
493495
sandbox2::monitor_base

sandboxed_api/sandbox2/executor.h

+5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737

3838
namespace sandbox2 {
3939

40+
// Forward declarations for friend declarations.
41+
class MonitorBase;
42+
class PtraceMonitor;
43+
class StackTracePeer;
44+
4045
// The sandbox2::Executor class is responsible for both creating and executing
4146
// new processes which will be sandboxed.
4247
class Executor final {

sandboxed_api/sandbox2/monitor_base.cc

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ MonitorBase::MonitorBase(Executor* executor, Policy* policy, Notify* notify)
134134
comms_(executor_->ipc()->comms()),
135135
ipc_(executor_->ipc()),
136136
uses_custom_forkserver_(executor_->fork_client_ != nullptr) {
137+
wait_for_execveat_ = executor->enable_sandboxing_pre_execve_;
137138
// It's a pre-connected Comms channel, no need to accept new connection.
138139
CHECK(comms_->IsConnected());
139140
std::string path =

sandboxed_api/sandbox2/monitor_base.h

+8
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ class MonitorBase {
8080
virtual void SetWallTimeLimit(absl::Duration limit) = 0;
8181

8282
protected:
83+
bool wait_for_execveat() const { return wait_for_execveat_; }
84+
void set_wait_for_execveat(bool wait_for_execve) {
85+
wait_for_execveat_ = wait_for_execve;
86+
}
87+
8388
void OnDone();
8489
// Sets basic info status and reason code in the result object.
8590
void SetExitStatusCode(Result::StatusEnum final_status,
@@ -168,6 +173,9 @@ class MonitorBase {
168173

169174
// Is the sandboxee forked from a custom forkserver?
170175
bool uses_custom_forkserver_;
176+
177+
// Are we waiting for the first execveat syscall?
178+
bool wait_for_execveat_ = false;
171179
};
172180

173181
} // namespace sandbox2

sandboxed_api/sandbox2/monitor_ptrace.cc

+5-13
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,7 @@ void CompleteSyscall(pid_t pid, int signo) {
138138
} // namespace
139139

140140
PtraceMonitor::PtraceMonitor(Executor* executor, Policy* policy, Notify* notify)
141-
: MonitorBase(executor, policy, notify),
142-
wait_for_execve_(executor->enable_sandboxing_pre_execve_) {
141+
: MonitorBase(executor, policy, notify) {
143142
if (executor_->limits()->wall_time_limit() != absl::ZeroDuration()) {
144143
auto deadline = absl::Now() + executor_->limits()->wall_time_limit();
145144
deadline_millis_.store(absl::ToUnixMillis(deadline),
@@ -151,13 +150,6 @@ PtraceMonitor::PtraceMonitor(Executor* executor, Policy* policy, Notify* notify)
151150
absl::GetFlag(FLAGS_sandbox2_monitor_ptrace_use_deadline_manager);
152151
}
153152

154-
bool PtraceMonitor::IsActivelyMonitoring() {
155-
// If we're still waiting for execve(), then we allow all syscalls.
156-
return !wait_for_execve_;
157-
}
158-
159-
void PtraceMonitor::SetActivelyMonitoring() { wait_for_execve_ = false; }
160-
161153
void PtraceMonitor::SetAdditionalResultInfo(std::unique_ptr<Regs> regs) {
162154
pid_t pid = regs->pid();
163155
result_.SetRegs(std::move(regs));
@@ -346,7 +338,7 @@ void PtraceMonitor::Run() {
346338
// all remaining processes (if there are any) because of the
347339
// PTRACE_O_EXITKILL ptrace() flag.
348340
if (ret == process_.main_pid) {
349-
if (IsActivelyMonitoring()) {
341+
if (!wait_for_execveat()) {
350342
SetExitStatusCode(Result::OK, WEXITSTATUS(status));
351343
} else {
352344
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_MONITOR);
@@ -632,7 +624,7 @@ bool PtraceMonitor::InitPtraceAttach() {
632624

633625
void PtraceMonitor::ActionProcessSyscall(Regs* regs, const Syscall& syscall) {
634626
// If the sandboxing is not enabled yet, allow the first __NR_execveat.
635-
if (syscall.nr() == __NR_execveat && !IsActivelyMonitoring()) {
627+
if (syscall.nr() == __NR_execveat && wait_for_execveat()) {
636628
VLOG(1) << "[PERMITTED/BEFORE_EXECVEAT]: " << "SYSCALL ::: PID: "
637629
<< regs->pid() << ", PROG: '" << util::GetProgName(regs->pid())
638630
<< "' : " << syscall.GetDescription();
@@ -783,10 +775,10 @@ void PtraceMonitor::EventPtraceNewProcess(pid_t pid, int event_msg) {
783775
}
784776

785777
void PtraceMonitor::EventPtraceExec(pid_t pid, int event_msg) {
786-
if (!IsActivelyMonitoring()) {
778+
if (wait_for_execveat()) {
787779
VLOG(1) << "PTRACE_EVENT_EXEC seen from PID: " << event_msg
788780
<< ". SANDBOX ENABLED!";
789-
SetActivelyMonitoring();
781+
set_wait_for_execveat(false);
790782
} else {
791783
// ptrace doesn't issue syscall-exit-stops for successful execve/execveat
792784
// system calls. Check if the monitor wanted to inspect the syscall's return

sandboxed_api/sandbox2/monitor_ptrace.h

-2
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,6 @@ class PtraceMonitor : public MonitorBase {
155155
bool timed_out_ = false;
156156
// Should we dump the main sandboxed PID's stack?
157157
bool should_dump_stack_ = false;
158-
// Is the sandboxee actively monitored, or maybe we're waiting for execve()?
159-
bool wait_for_execve_;
160158
// Syscalls that are running, whose result values we want to inspect.
161159
absl::flat_hash_map<pid_t, Syscall> syscalls_in_progress_;
162160
sigset_t sset_;

sandboxed_api/sandbox2/monitor_unotify.cc

+12-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "sandboxed_api/sandbox2/notify.h"
4141
#include "sandboxed_api/sandbox2/policy.h"
4242
#include "sandboxed_api/sandbox2/result.h"
43+
#include "sandboxed_api/sandbox2/util.h"
4344
#include "sandboxed_api/sandbox2/util/seccomp_unotify.h"
4445
#include "sandboxed_api/util/fileops.h"
4546
#include "sandboxed_api/util/status_macros.h"
@@ -189,13 +190,23 @@ void UnotifyMonitor::HandleUnotify() {
189190
return;
190191
}
191192
}
193+
Syscall syscall(*req_data);
194+
if (wait_for_execveat() && syscall.nr() == __NR_execveat &&
195+
util::SeccompUnotify::IsContinueSupported()) {
196+
VLOG(1) << "[PERMITTED/BEFORE_EXECVEAT]: " << "SYSCALL ::: PID: "
197+
<< syscall.pid() << ", PROG: '" << util::GetProgName(syscall.pid())
198+
<< "' : " << syscall.GetDescription();
199+
set_wait_for_execveat(false);
200+
AllowSyscallViaUnotify(*req_data);
201+
return;
202+
}
192203
absl::StatusOr<uint32_t> policy_ret =
193204
bpf::Evaluate(original_policy_, req_data->data);
194205
if (!policy_ret.ok()) {
195206
LOG(ERROR) << "Failed to evaluate policy: " << policy_ret.status();
196207
SetExitStatusCode(Result::INTERNAL_ERROR, Result::FAILED_NOTIFY);
197208
}
198-
Syscall syscall(*req_data);
209+
199210
const sock_filter trace_action = SANDBOX2_TRACE;
200211
bool should_trace = *policy_ret == trace_action.k;
201212
Notify::TraceAction trace_response = Notify::TraceAction::kDeny;

sandboxed_api/sandbox2/policy.cc

+6-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "sandboxed_api/sandbox2/syscall.h"
4141
#include "sandboxed_api/sandbox2/util.h"
4242
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
43+
#include "sandboxed_api/sandbox2/util/seccomp_unotify.h"
4344

4445
#ifndef SECCOMP_FILTER_FLAG_NEW_LISTENER
4546
#define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3)
@@ -112,6 +113,10 @@ std::vector<sock_filter> Policy::GetDefaultPolicy(
112113

113114
std::vector<sock_filter> policy;
114115
if (user_notif) {
116+
sock_filter execve_action = ALLOW;
117+
if (util::SeccompUnotify::IsContinueSupported()) {
118+
execve_action = BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_USER_NOTIF);
119+
}
115120
policy = {
116121
// If compiled arch is different from the runtime one, inform the
117122
// Monitor.
@@ -134,7 +139,7 @@ std::vector<sock_filter> Policy::GetDefaultPolicy(
134139
JNE32(AT_EMPTY_PATH, JUMP(&l, past_execveat_l)),
135140
ARG_32(5),
136141
JNE32(internal::kExecveMagic, JUMP(&l, past_execveat_l)),
137-
ALLOW,
142+
execve_action,
138143
LABEL(&l, past_execveat_l),
139144
LOAD_SYSCALL_NR,
140145
});

sandboxed_api/sandbox2/policy_test.cc

+16-2
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,11 @@ TEST_P(PolicyTest, DetectSandboxSyscall) {
381381
EXPECT_THAT(result.reason_code(), Eq(0));
382382
}
383383

384-
TEST_P(PolicyTest, ExecveatAllowedByDefault) {
384+
TEST_P(PolicyTest, ExecveatNotAllowedByDefault) {
385385
const std::string path = GetTestSourcePath("sandbox2/testcases/execveat");
386386

387387
std::unique_ptr<Sandbox2> s2 = CreateTestSandbox(
388-
{path},
388+
{path, "1"},
389389
CreateDefaultPermissiveTestPolicy(path).BlockSyscallWithErrno(
390390
__NR_execveat, EPERM),
391391
/*sandbox_pre_execve=*/false);
@@ -396,6 +396,20 @@ TEST_P(PolicyTest, ExecveatAllowedByDefault) {
396396
EXPECT_THAT(result.reason_code(), Eq(0));
397397
}
398398

399+
TEST_P(PolicyTest, SecondExecveatNotAllowedByDefault) {
400+
const std::string path = GetTestSourcePath("sandbox2/testcases/execveat");
401+
402+
std::unique_ptr<Sandbox2> s2 = CreateTestSandbox(
403+
{path, "2"},
404+
CreateDefaultPermissiveTestPolicy(path).BlockSyscallWithErrno(
405+
__NR_execveat, EPERM));
406+
Result result = s2->Run();
407+
408+
// The test binary should exit with success.
409+
ASSERT_THAT(result.final_status(), Eq(Result::OK));
410+
EXPECT_THAT(result.reason_code(), Eq(0));
411+
}
412+
399413
INSTANTIATE_TEST_SUITE_P(Sandbox2, PolicyTest, ::testing::Values(false, true),
400414
[](const ::testing::TestParamInfo<bool>& info) {
401415
return info.param ? "UnotifyMonitor"

sandboxed_api/sandbox2/testcases/execveat.cc

+6-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@
2424
#include "sandboxed_api/sandbox2/util.h"
2525

2626
int main(int argc, char** argv) {
27-
sandbox2::Comms comms(sandbox2::Comms::kSandbox2ClientCommsFD);
28-
sandbox2::Client client(&comms);
29-
client.SandboxMeHere();
27+
int testno = atoi(argv[1]); // NOLINT
28+
if (testno == 1) {
29+
sandbox2::Comms comms(sandbox2::Comms::kSandbox2ClientCommsFD);
30+
sandbox2::Client client(&comms);
31+
client.SandboxMeHere();
32+
}
3033
int result =
3134
sandbox2::util::Syscall(__NR_execveat, AT_EMPTY_PATH, 0, 0, 0, 0);
3235
if (result != -1 || errno != EPERM) {

0 commit comments

Comments
 (0)