Skip to content

Commit 2c6a392

Browse files
happyCoder92copybara-github
authored andcommitted
Refactor comms around client config
Use protobuf for simplicity and extensibility. PiperOrigin-RevId: 907573418 Change-Id: Ia7dc0a6e6097d1bd6c367010ad171029bdcc95db
1 parent 91e130e commit 2c6a392

14 files changed

Lines changed: 232 additions & 159 deletions

sandboxed_api/sandbox2/BUILD

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ cc_library(
357357
deps = [
358358
":buffer",
359359
":client",
360+
":client_cc_proto",
360361
":comms",
361362
":executor",
362363
":fork_client",
@@ -442,6 +443,7 @@ cc_library(
442443
copts = sapi_platform_copts(),
443444
deps = [
444445
":client",
446+
":client_cc_proto",
445447
":comms",
446448
":executor",
447449
":flags",
@@ -483,6 +485,7 @@ cc_library(
483485
deps = [
484486
":bpf_evaluator",
485487
":client",
488+
":client_cc_proto",
486489
":executor",
487490
":flags",
488491
":forkserver_cc_proto",
@@ -520,6 +523,7 @@ cc_library(
520523
copts = sapi_platform_copts(),
521524
deps = [
522525
":client",
526+
":client_cc_proto",
523527
":comms",
524528
":executor",
525529
":flags",
@@ -593,6 +597,11 @@ cc_library(
593597
],
594598
)
595599

600+
sapi_proto_library(
601+
name = "client_proto",
602+
srcs = ["client.proto"],
603+
)
604+
596605
# Should be used in sandboxee code instead of :sandbox2 if it uses just
597606
# sandbox2::Client::SandboxMeHere() and sandbox2::Comms
598607
cc_library(
@@ -603,6 +612,7 @@ cc_library(
603612
visibility = ["//visibility:public"],
604613
deps = [
605614
":buffer",
615+
":client_cc_proto",
606616
":comms",
607617
":logsink",
608618
":policy",

sandboxed_api/sandbox2/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ target_link_libraries(sandbox2_monitor_base
438438
PUBLIC absl::status
439439
absl::statusor
440440
absl::synchronization
441+
sandbox2::client_proto
441442
sandbox2::comms
442443
sandbox2::executor
443444
sandbox2::fork_client
@@ -474,6 +475,7 @@ target_link_libraries(sandbox2_monitor_ptrace
474475
sapi::fileops
475476
sapi::status
476477
sandbox2::client
478+
sandbox2::client_proto
477479
sandbox2::comms
478480
sandbox2::result
479481
sandbox2::sanitizer
@@ -515,6 +517,7 @@ target_link_libraries(sandbox2_monitor_unotify
515517
sapi::base
516518
sandbox2::bpf_evaluator
517519
sandbox2::client
520+
sandbox2::client_proto
518521
sandbox2::flags
519522
sandbox2::forkserver_proto
520523
sandbox2::seccomp_unotify
@@ -569,6 +572,20 @@ target_link_libraries(sandbox2_policybuilder
569572
sandbox2::policy
570573
)
571574

575+
# sandboxed_api/sandbox2:client_proto
576+
sapi_protobuf_generate_cpp(_sandbox2_client_pb_h _sandbox2_client_pb_cc
577+
client.proto
578+
)
579+
add_library(sandbox2_client_proto ${SAPI_LIB_TYPE}
580+
${_sandbox2_client_pb_cc}
581+
${_sandbox2_client_pb_h}
582+
)
583+
add_library(sandbox2::client_proto ALIAS sandbox2_client_proto)
584+
target_link_libraries(sandbox2_client_proto PRIVATE
585+
protobuf::libprotobuf
586+
sapi::base
587+
)
588+
572589
# sandboxed_api/sandbox2:client
573590
add_library(sandbox2_client ${SAPI_LIB_TYPE}
574591
client.cc
@@ -591,6 +608,7 @@ target_link_libraries(sandbox2_client
591608
sapi::status
592609
PUBLIC absl::flat_hash_map
593610
absl::status
611+
sandbox2::client_proto
594612
sandbox2::comms
595613
sandbox2::logsink
596614
sandbox2::network_proxy_client

sandboxed_api/sandbox2/client.cc

Lines changed: 35 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "absl/strings/string_view.h"
5151
#include "sandboxed_api/config.h"
5252
#include "sandboxed_api/sandbox2/buffer.h"
53+
#include "sandboxed_api/sandbox2/client.pb.h"
5354
#include "sandboxed_api/sandbox2/comms.h"
5455
#include "sandboxed_api/sandbox2/logsink.h"
5556
#include "sandboxed_api/sandbox2/network_proxy/client.h"
@@ -189,6 +190,7 @@ std::string Client::GetFdMapEnvVar() const {
189190
}
190191

191192
void Client::PrepareEnvironment(int* preserved_fd) {
193+
ReceiveClientConfig();
192194
SetUpIPC(preserved_fd);
193195
SetUpCwd();
194196
}
@@ -204,41 +206,19 @@ void Client::SandboxMeHere() {
204206
}
205207

206208
void Client::SetUpCwd() {
207-
{
208-
// Get the current working directory to check if we are in a mount
209-
// namespace.
210-
// Note: glibc 2.27 no longer returns a relative path in that case, but
211-
// fails with ENOENT and returns a nullptr instead. The code still
212-
// needs to run on lower version for the time being.
213-
char cwd_buf[PATH_MAX + 1] = {0};
214-
char* cwd = getcwd(cwd_buf, ABSL_ARRAYSIZE(cwd_buf));
215-
SAPI_RAW_PCHECK(cwd != nullptr || errno == ENOENT,
216-
"no current working directory");
217-
218-
// Outside of the mount namespace, the path is of the form
219-
// '(unreachable)/...'. Only check for the slash, since Linux might make up
220-
// other prefixes in the future.
221-
if (errno == ENOENT || cwd_buf[0] != '/') {
222-
SAPI_RAW_VLOG(1, "chdir into mount namespace, cwd was '%s'", cwd_buf);
223-
// If we are in a mount namespace but fail to chdir, then it can lead to a
224-
// sandbox escape -- we need to fail with FATAL if the chdir fails.
225-
SAPI_RAW_PCHECK(chdir("/") != -1, "corrective chdir");
226-
}
227-
}
228-
229209
// Receive the user-supplied current working directory and change into it.
230-
std::string cwd;
231-
SAPI_RAW_CHECK(comms_->RecvString(&cwd), "receiving working directory");
232-
if (!cwd.empty()) {
233-
// On the other hand this chdir can fail without a sandbox escape. It will
234-
// probably not have the intended behavior though.
235-
if (chdir(cwd.c_str()) == -1 && SAPI_RAW_VLOG_IS_ON(1)) {
236-
SAPI_RAW_PLOG(
237-
INFO,
238-
"chdir(%s) failed, falling back to previous cwd or / (with "
239-
"namespaces). Use Executor::SetCwd() to set a working directory",
240-
cwd.c_str());
241-
}
210+
if (client_config_.cwd().empty()) {
211+
return;
212+
}
213+
std::string cwd(client_config_.cwd());
214+
// This chdir can fail without a sandbox escape. It will probably not have
215+
// the intended behavior though.
216+
if (chdir(cwd.c_str()) == -1 && SAPI_RAW_VLOG_IS_ON(1)) {
217+
SAPI_RAW_PLOG(
218+
INFO,
219+
"chdir(%s) failed, falling back to previous cwd or / (with "
220+
"namespaces). Use Executor::SetCwd() to set a working directory",
221+
cwd.c_str());
242222
}
243223
}
244224

@@ -314,9 +294,12 @@ void Client::SetUpIPC(int* preserved_fd) {
314294
}
315295

316296
void Client::ReceivePolicy() {
317-
std::vector<uint8_t> bytes;
318-
SAPI_RAW_CHECK(comms_->RecvBytes(&bytes), "receive bytes");
319-
policy_ = std::move(bytes);
297+
SAPI_RAW_CHECK(comms_->RecvBytes(&policy_), "receive bytes");
298+
}
299+
300+
void Client::ReceiveClientConfig() {
301+
SAPI_RAW_CHECK(comms_->RecvProtoBuf(&client_config_),
302+
"receive client config");
320303
}
321304

322305
void Client::ApplyPolicyAndBecomeTracee() {
@@ -325,18 +308,13 @@ void Client::ApplyPolicyAndBecomeTracee() {
325308
// this function does nothing.
326309
sanitizer::WaitForSanitizer();
327310

328-
// Creds can be received w/o synchronization, once the connection is
329-
// established.
330-
pid_t cred_pid;
331-
uid_t cred_uid ABSL_ATTRIBUTE_UNUSED;
332-
gid_t cred_gid ABSL_ATTRIBUTE_UNUSED;
333-
SAPI_RAW_CHECK(comms_->GetPeerCreds(&cred_pid, &cred_uid, &cred_gid),
334-
"receiving credentials");
335-
336-
SAPI_RAW_CHECK(prctl(PR_SET_DUMPABLE, 1) == 0,
337-
"setting PR_SET_DUMPABLE flag");
338-
if (prctl(PR_SET_PTRACER, cred_pid) == -1) {
339-
SAPI_RAW_VLOG(1, "No YAMA on this system. Continuing");
311+
pid_t ptracer_tid = client_config_.ptracer_tid();
312+
if (ptracer_tid > 0) {
313+
SAPI_RAW_CHECK(prctl(PR_SET_DUMPABLE, 1) == 0,
314+
"setting PR_SET_DUMPABLE flag");
315+
if (prctl(PR_SET_PTRACER, ptracer_tid) == -1) {
316+
SAPI_RAW_VLOG(1, "No YAMA on this system. Continuing");
317+
}
340318
}
341319

342320
SAPI_RAW_CHECK(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == 0,
@@ -364,7 +342,9 @@ void Client::ApplyPolicyAndBecomeTracee() {
364342
uint32_t message; // wait for confirmation
365343
SAPI_RAW_CHECK(comms_->RecvUint32(&message),
366344
"receving confirmation from executor");
367-
bool allow_speculation = message & kAllowSpeculationBit;
345+
SAPI_RAW_CHECK(message == kSandbox2MonitorReady,
346+
"invalid confirmation from executor");
347+
bool allow_speculation = client_config_.seccomp_allow_speculation();
368348
if (!allow_speculation) {
369349
if constexpr (sapi::host_cpu::IsX8664() || sapi::host_cpu::IsArm64()) {
370350
SAPI_RAW_PCHECK(prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS,
@@ -381,12 +361,13 @@ void Client::ApplyPolicyAndBecomeTracee() {
381361
}
382362
uint32_t seccomp_extra_flags =
383363
allow_speculation ? SECCOMP_FILTER_FLAG_SPEC_ALLOW : 0;
384-
uint32_t monitor_type = message & kMonitorTypeMask;
385-
if (monitor_type == kSandbox2ClientUnotify) {
364+
if (client_config_.monitor_type() == ClientConfig::CLIENT_MONITOR_UNOTIFY) {
386365
InitSeccompUnotify(prog, comms_, seccomp_extra_flags);
387366
} else {
388-
SAPI_RAW_CHECK(monitor_type == kSandbox2ClientPtrace,
389-
"invalid confirmation from executor");
367+
SAPI_RAW_CHECK(
368+
client_config_.monitor_type() == ClientConfig::CLIENT_MONITOR_PTRACE,
369+
absl::StrCat("invalid monitor type: ", client_config_.monitor_type())
370+
.c_str());
390371
InitSeccompRegular(prog, seccomp_extra_flags);
391372
}
392373
}

sandboxed_api/sandbox2/client.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "absl/status/status.h"
2828
#include "absl/status/statusor.h"
2929
#include "sandboxed_api/sandbox2/buffer.h"
30+
#include "sandboxed_api/sandbox2/client.pb.h"
3031
#include "sandboxed_api/sandbox2/comms.h"
3132
#include "sandboxed_api/sandbox2/logsink.h"
3233
#include "sandboxed_api/sandbox2/network_proxy/client.h"
@@ -38,12 +39,8 @@ class Client {
3839
// Client is ready to be sandboxed.
3940
static constexpr uint32_t kClient2SandboxReady = 0x0A0B0C01;
4041

41-
// Sandbox is ready to monitor the sandboxee. Used with PtraceMonitor.
42-
static constexpr uint32_t kSandbox2ClientPtrace = 0x0A0B0C02;
43-
44-
// Sandboxee should setup seccomp_unotify and send back the FD. Used with
45-
// UnotifyMonitor.
46-
static constexpr uint32_t kSandbox2ClientUnotify = 0x0A0B0C03;
42+
// Sandbox is ready to monitor the sandboxee.
43+
static constexpr uint32_t kSandbox2MonitorReady = 0x0A0B0D01;
4744

4845
// Sandboxee can proceed after seccomp_unotify setup as limits have been
4946
// applied by the monitor.
@@ -97,6 +94,9 @@ class Client {
9794

9895
friend class ForkServer;
9996

97+
// Client configuration received from the monitor.
98+
ClientConfig client_config_;
99+
100100
// Seccomp-bpf policy received from the monitor.
101101
std::vector<uint8_t> policy_;
102102

@@ -130,6 +130,9 @@ class Client {
130130
// Receives seccomp-bpf policy from the monitor.
131131
void ReceivePolicy();
132132

133+
// Receives client configuration from the monitor.
134+
void ReceiveClientConfig();
135+
133136
// Applies sandbox-bpf policy, have limits applied on us, and become ptrace'd.
134137
void ApplyPolicyAndBecomeTracee();
135138

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
edition = "2024";
16+
17+
package sandbox2;
18+
19+
message ClientConfig {
20+
enum MonitorType {
21+
// Default value
22+
CLIENT_MONITOR_UNSPECIFIED = 0;
23+
// Ptrace based monitor
24+
CLIENT_MONITOR_PTRACE = 1;
25+
// Seccomp_unotify based monitor
26+
CLIENT_MONITOR_UNOTIFY = 2;
27+
}
28+
// Monitor type used by the sandbox
29+
MonitorType monitor_type = 1;
30+
31+
string cwd = 2;
32+
33+
int32 ptracer_tid = 3;
34+
35+
bool seccomp_allow_speculation = 4;
36+
}

sandboxed_api/sandbox2/monitor_base.cc

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,12 @@ void MonitorBase::Launch() {
220220
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_NOTIFY);
221221
return;
222222
}
223-
if (!InitSendIPC()) {
224-
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_IPC);
223+
if (!InitSendClientConfig()) {
224+
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_CLIENT_CONFIG);
225225
return;
226226
}
227-
if (!InitSendCwd()) {
228-
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_CWD);
227+
if (!InitSendIPC()) {
228+
SetExitStatusCode(Result::SETUP_ERROR, Result::FAILED_IPC);
229229
return;
230230
}
231231
if (!InitSendPolicy()) {
@@ -267,12 +267,8 @@ absl::Status MonitorBase::SendPolicy(const std::vector<sock_filter>& policy) {
267267
return absl::OkStatus();
268268
}
269269

270-
bool MonitorBase::SendMonitorReadyMessageAndFlags(uint32_t monitor_type) {
271-
uint32_t message = monitor_type;
272-
if (policy_->allow_speculation_) {
273-
message |= Client::kAllowSpeculationBit;
274-
}
275-
return comms_->SendUint32(message);
270+
bool MonitorBase::SendMonitorReadyMessage() {
271+
return comms_->SendUint32(Client::kSandbox2MonitorReady);
276272
}
277273

278274
bool MonitorBase::InitSendPolicy() {
@@ -287,15 +283,6 @@ bool MonitorBase::InitSendPolicy() {
287283
return true;
288284
}
289285

290-
bool MonitorBase::InitSendCwd() {
291-
if (!comms_->SendString(executor_->cwd_)) {
292-
PLOG(ERROR) << "Couldn't send cwd";
293-
return false;
294-
}
295-
296-
return true;
297-
}
298-
299286
bool MonitorBase::InitApplyLimit(pid_t pid, int resource,
300287
const rlimit64& rlim) const {
301288
using RlimitResource = __rlimit_resource;
@@ -335,6 +322,21 @@ bool MonitorBase::InitApplyLimits() {
335322
InitApplyLimit(process_.main_pid, RLIMIT_CORE, limits->rlimit_core());
336323
}
337324

325+
ClientConfig MonitorBase::CreateClientConfig() const {
326+
ClientConfig client_config;
327+
client_config.set_cwd(executor_->cwd_);
328+
client_config.set_seccomp_allow_speculation(policy_->allow_speculation_);
329+
return client_config;
330+
}
331+
332+
bool MonitorBase::InitSendClientConfig() {
333+
if (!comms_->SendProtoBuf(CreateClientConfig())) {
334+
LOG(ERROR) << "Couldn't send client config";
335+
return false;
336+
}
337+
return true;
338+
}
339+
338340
bool MonitorBase::InitSendIPC() { return ipc_->SendFdsOverComms(); }
339341

340342
bool MonitorBase::WaitForSandboxReady() {

0 commit comments

Comments
 (0)