Skip to content

Commit c99e85d

Browse files
Sandboxed API Teamcopybara-github
authored andcommitted
Add a Backend template parameter to the Sandbox class
To allow other sandbox backends, we move all sandbox2 specific code into its own Sandbox2Backend class. PiperOrigin-RevId: 869247483 Change-Id: I46bf7a828ed526257367f10135a1474a982c8234
1 parent 45b3a34 commit c99e85d

8 files changed

Lines changed: 567 additions & 366 deletions

File tree

sandboxed_api/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ cc_library(
6969
name = "sapi",
7070
srcs = [
7171
"sandbox.cc",
72+
"sandbox2_backend.cc",
7273
"sandbox2_rpcchannel.cc",
7374
"sandbox2_rpcchannel.h",
7475
"transaction.cc",
@@ -78,6 +79,8 @@ cc_library(
7879
# supports this usecase.
7980
"embed_file.h",
8081
"sandbox.h",
82+
"sandbox2_backend.h",
83+
"sandbox_config.h",
8184
"transaction.h",
8285
],
8386
copts = sapi_platform_copts(),

sandboxed_api/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ target_link_libraries(sapi_embed_file
5353
add_library(sapi_sapi ${SAPI_LIB_TYPE}
5454
sandbox.cc
5555
sandbox.h
56+
sandbox2_backend.cc
57+
sandbox2_backend.h
58+
sandbox_config.h
5659
sandbox2_rpcchannel.cc
5760
sandbox2_rpcchannel.h
5861
transaction.cc

sandboxed_api/sandbox.cc

Lines changed: 13 additions & 229 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,7 @@
2727
#include <initializer_list>
2828
#include <memory>
2929
#include <string>
30-
#include <utility>
31-
#include <variant>
32-
#include <vector>
3330

34-
#include "sandboxed_api/file_toc.h"
3531
#include "absl/base/dynamic_annotations.h"
3632
#include "absl/base/macros.h"
3733
#include "absl/log/check.h"
@@ -41,23 +37,14 @@
4137
#include "absl/strings/str_cat.h"
4238
#include "absl/strings/str_format.h"
4339
#include "absl/strings/string_view.h"
44-
#include "absl/synchronization/mutex.h"
4540
#include "absl/time/time.h"
4641
#include "absl/types/span.h"
4742
#include "sandboxed_api/call.h"
4843
#include "sandboxed_api/config.h"
49-
#include "sandboxed_api/embed_file.h"
5044
#include "sandboxed_api/rpcchannel.h"
51-
#include "sandboxed_api/sandbox2/executor.h"
52-
#include "sandboxed_api/sandbox2/fork_client.h"
5345
#include "sandboxed_api/sandbox2/limits.h"
5446
#include "sandboxed_api/sandbox2/policy.h"
5547
#include "sandboxed_api/sandbox2/policybuilder.h"
56-
#include "sandboxed_api/sandbox2/result.h"
57-
#include "sandboxed_api/sandbox2/sandbox2.h"
58-
#include "sandboxed_api/sandbox2_rpcchannel.h"
59-
#include "sandboxed_api/util/path.h"
60-
#include "sandboxed_api/util/runfiles.h"
6148
#include "sandboxed_api/util/status_macros.h"
6249
#include "sandboxed_api/var_abstract.h"
6350
#include "sandboxed_api/var_array.h"
@@ -68,16 +55,6 @@
6855

6956
namespace sapi {
7057

71-
Sandbox::Sandbox(SandboxConfig config) : config_(std::move(config)) {
72-
CHECK(config_.sandbox2.fork_client_context.has_value());
73-
}
74-
75-
Sandbox::~Sandbox() {
76-
Terminate();
77-
// The forkserver will die automatically when the executor goes out of scope
78-
// and closes the comms object.
79-
}
80-
8158
// IMPORTANT: This policy must be safe to use with
8259
// `Allow(sandbox2::UnrestrictedNetworking())`.
8360
sandbox2::PolicyBuilder Sandbox2Config::DefaultPolicyBuilder() {
@@ -132,198 +109,21 @@ sandbox2::Limits Sandbox2Config::DefaultLimits() {
132109
return limits;
133110
}
134111

135-
void Sandbox::Terminate(bool attempt_graceful_exit) {
136-
if (!is_active()) {
137-
return;
138-
}
139-
140-
absl::StatusOr<sandbox2::Result> result;
141-
if (attempt_graceful_exit) {
142-
if (absl::Status requested_exit = rpc_channel_->Exit();
143-
!requested_exit.ok()) {
144-
LOG(WARNING)
145-
<< "rpc_channel->Exit() failed, calling AwaitResultWithTimeout(1) "
146-
<< requested_exit;
147-
}
148-
result = s2_->AwaitResultWithTimeout(absl::Seconds(1));
149-
if (!result.ok()) {
150-
LOG(WARNING) << "s2_->AwaitResultWithTimeout failed, status: "
151-
<< result.status() << " Killing PID: " << pid();
152-
}
153-
}
154-
155-
if (!attempt_graceful_exit || !result.ok()) {
156-
s2_->Kill();
157-
result = s2_->AwaitResult();
158-
}
159-
160-
if ((result->final_status() == sandbox2::Result::OK &&
161-
result->reason_code() == 0) ||
162-
(!attempt_graceful_exit &&
163-
result->final_status() == sandbox2::Result::EXTERNAL_KILL)) {
164-
VLOG(2) << "Sandbox2 finished with: " << result->ToString();
165-
} else {
166-
LOG(WARNING) << "Sandbox2 finished with: " << result->ToString();
167-
}
168-
}
169-
170-
static std::string PathToSAPILib(const std::string& lib_path) {
171-
return file::IsAbsolutePath(lib_path) ? lib_path
172-
: GetDataDependencyFilePath(lib_path);
173-
}
174-
175-
void Sandbox::ApplySandbox2Config(sandbox2::Executor* executor) const {
176-
const Sandbox2Config& config = config_.sandbox2;
177-
if (config.enable_log_server) {
178-
executor->ipc()->EnableLogServer();
179-
}
180-
if (config.cwd.has_value()) {
181-
executor->set_cwd(*config.cwd);
182-
}
183-
if (config.limits.has_value()) {
184-
*executor->limits() = *config.limits;
185-
}
186-
}
187-
188-
void Sandbox::MapFileDescriptors(sandbox2::Executor* executor) const {
189-
if (!config_.fd_mappings.has_value()) {
190-
return;
191-
}
192-
for (const auto& [host_fd, sandbox_fd] : *config_.fd_mappings) {
193-
executor->ipc()->MapDupedFd(host_fd.get(), sandbox_fd);
194-
}
195-
}
196-
197-
absl::Status Sandbox::Init() {
198-
// It's already initialized
199-
if (is_active()) {
200-
return absl::OkStatus();
201-
}
202-
203-
std::shared_ptr<sandbox2::Executor> fork_client_executor;
204-
std::shared_ptr<sandbox2::ForkClient> fork_client;
205-
{
206-
absl::MutexLock lock(fork_client_shared().mu_);
207-
// Initialize the forkserver if it is not already running.
208-
if (!fork_client_shared().client_) {
209-
auto sandboxee_source = fork_client_context().sandboxee_source_;
210-
211-
std::string lib_path;
212-
int embed_lib_fd = -1;
213-
if (std::holds_alternative<const FileToc*>(sandboxee_source)) {
214-
const FileToc* embed_lib_toc =
215-
std::get<const FileToc*>(sandboxee_source);
216-
embed_lib_fd = EmbedFile::instance()->GetDupFdForFileToc(embed_lib_toc);
217-
if (embed_lib_fd == -1) {
218-
PLOG(ERROR) << "Cannot create executable FD for TOC:'"
219-
<< embed_lib_toc->name << "'";
220-
return absl::UnavailableError("Could not create executable FD");
221-
}
222-
lib_path = embed_lib_toc->name;
223-
} else {
224-
lib_path = PathToSAPILib(std::get<std::string>(sandboxee_source));
225-
if (lib_path.empty()) {
226-
LOG(ERROR) << "SAPI library path is empty";
227-
return absl::FailedPreconditionError("No SAPI library path given");
228-
}
229-
}
230-
std::vector<std::string> args = {lib_path};
231-
// Additional arguments, if needed.
232-
auto flags =
233-
config_.command_line_flags.value_or(SandboxConfig::DefaultFlags());
234-
for (const auto& [key, value] : flags) {
235-
args.push_back(absl::StrCat("--", key, "=", value));
236-
}
237-
238-
fork_client_shared().executor_ =
239-
(embed_lib_fd >= 0) ? std::make_shared<sandbox2::Executor>(
240-
embed_lib_fd, args, EnvironmentVariables())
241-
: std::make_shared<sandbox2::Executor>(
242-
lib_path, args, EnvironmentVariables());
243-
244-
fork_client_shared().client_ =
245-
fork_client_shared().executor_->StartForkServer();
246-
247-
if (!fork_client_shared().client_) {
248-
LOG(ERROR) << "Could not start forkserver";
249-
return absl::UnavailableError("Could not start the forkserver");
250-
}
251-
}
252-
fork_client_executor = fork_client_shared().executor_;
253-
fork_client = fork_client_shared().client_;
254-
}
255-
256-
std::unique_ptr<sandbox2::Policy> s2p;
257-
if (config_.sandbox2.policy) {
258-
s2p = std::make_unique<sandbox2::Policy>(*config_.sandbox2.policy);
259-
} else {
260-
sandbox2::PolicyBuilder policy_builder =
261-
Sandbox2Config::DefaultPolicyBuilder();
262-
if (config_.sandbox2.use_unotify_monitor) {
263-
policy_builder.CollectStacktracesOnSignal(false);
264-
}
265-
s2p = policy_builder.BuildOrDie();
266-
}
267-
268-
// Spawn new process from the forkserver.
269-
auto executor = std::make_unique<sandbox2::Executor>(fork_client.get());
270-
271-
executor
272-
// The client.cc code is capable of enabling sandboxing on its own.
273-
->set_enable_sandbox_before_exec(false)
274-
// By default, set cwd to "/", can be changed in ModifyExecutor().
275-
.set_cwd("/");
276-
// Disable time limits.
277-
*executor->limits() = Sandbox2Config::DefaultLimits();
278-
279-
// Modify the executor, e.g. by setting custom limits and IPC.
280-
ApplySandbox2Config(executor.get());
281-
MapFileDescriptors(executor.get());
282-
283-
s2_ = std::make_unique<sandbox2::Sandbox2>(std::move(executor),
284-
std::move(s2p), CreateNotifier());
285-
if (config_.sandbox2.use_unotify_monitor) {
286-
SAPI_RETURN_IF_ERROR(s2_->EnableUnotifyMonitor());
287-
}
288-
s2_awaited_ = false;
289-
auto res = s2_->RunAsync();
290-
291-
comms_ = s2_->comms();
292-
pid_ = s2_->pid();
293-
294-
rpc_channel_ = std::make_unique<Sandbox2RPCChannel>(comms_, pid_);
295-
296-
if (!res) {
297-
// Allow recovering from a bad fork client state.
298-
{
299-
absl::MutexLock lock(fork_client_shared().mu_);
300-
fork_client_shared().client_.reset();
301-
}
302-
sandbox2::Result result = s2_->AwaitResult();
303-
LOG(ERROR) << "Could not start the sandbox: " << result.ToString();
304-
return absl::UnavailableError(
305-
absl::StrCat("Could not start the sandbox: ", result.ToString()));
306-
}
307-
return absl::OkStatus();
308-
}
309-
310-
bool Sandbox::is_active() const { return s2_ && !s2_->IsTerminated(); }
311-
312-
absl::Status Sandbox::Allocate(v::Var* var, bool automatic_free) {
112+
absl::Status SandboxBase::Allocate(v::Var* var, bool automatic_free) {
313113
if (!is_active()) {
314114
return absl::UnavailableError("Sandbox not active");
315115
}
316116
return var->Allocate(rpc_channel(), automatic_free);
317117
}
318118

319-
absl::Status Sandbox::Free(v::Var* var) {
119+
absl::Status SandboxBase::Free(v::Var* var) {
320120
if (!is_active()) {
321121
return absl::UnavailableError("Sandbox not active");
322122
}
323123
return var->Free(rpc_channel());
324124
}
325125

326-
absl::Status Sandbox::SynchronizePtrBefore(v::Ptr* p) {
126+
absl::Status SandboxBase::SynchronizePtrBefore(v::Ptr* p) {
327127
if (!is_active()) {
328128
return absl::UnavailableError("Sandbox not active");
329129
}
@@ -352,7 +152,7 @@ absl::Status Sandbox::SynchronizePtrBefore(v::Ptr* p) {
352152
return p->GetPointedVar()->TransferToSandboxee(rpc_channel());
353153
}
354154

355-
absl::Status Sandbox::SynchronizePtrAfter(v::Ptr* p) const {
155+
absl::Status SandboxBase::SynchronizePtrAfter(v::Ptr* p) const {
356156
if (!is_active()) {
357157
return absl::UnavailableError("Sandbox not active");
358158
}
@@ -377,7 +177,7 @@ absl::Status Sandbox::SynchronizePtrAfter(v::Ptr* p) const {
377177
return p->GetPointedVar()->TransferFromSandboxee(rpc_channel());
378178
}
379179

380-
absl::Status Sandbox::Call(
180+
absl::Status SandboxBase::Call(
381181
const std::string& func, v::Callable* ret,
382182
std::initializer_list<internal::PtrOrCallable> args) {
383183
if (!is_active()) {
@@ -446,7 +246,7 @@ absl::Status Sandbox::Call(
446246
// Call & receive data.
447247
FuncRet fret;
448248
SAPI_RETURN_IF_ERROR(
449-
rpc_channel_->Call(rfcall, comms::kMsgCall, &fret, rfcall.ret_type));
249+
rpc_channel()->Call(rfcall, comms::kMsgCall, &fret, rfcall.ret_type));
450250

451251
if (fret.ret_type == v::Type::kFloat) {
452252
memcpy(ret->GetLocal(), &fret.float_val,
@@ -473,38 +273,38 @@ absl::Status Sandbox::Call(
473273
return absl::OkStatus();
474274
}
475275

476-
absl::Status Sandbox::Symbol(const char* symname, void** addr) {
276+
absl::Status SandboxBase::Symbol(const char* symname, void** addr) {
477277
if (!is_active()) {
478278
return absl::UnavailableError("Sandbox not active");
479279
}
480-
return rpc_channel_->Symbol(symname, addr);
280+
return rpc_channel()->Symbol(symname, addr);
481281
}
482282

483-
absl::Status Sandbox::TransferToSandboxee(v::Var* var) {
283+
absl::Status SandboxBase::TransferToSandboxee(v::Var* var) {
484284
if (!is_active()) {
485285
return absl::UnavailableError("Sandbox not active");
486286
}
487287
return var->TransferToSandboxee(rpc_channel());
488288
}
489289

490-
absl::Status Sandbox::TransferFromSandboxee(v::Var* var) {
290+
absl::Status SandboxBase::TransferFromSandboxee(v::Var* var) {
491291
if (!is_active()) {
492292
return absl::UnavailableError("Sandbox not active");
493293
}
494294
return var->TransferFromSandboxee(rpc_channel());
495295
}
496296

497297
absl::StatusOr<std::unique_ptr<sapi::v::Array<const uint8_t>>>
498-
Sandbox::AllocateAndTransferToSandboxee(absl::Span<const uint8_t> buffer) {
298+
SandboxBase::AllocateAndTransferToSandboxee(absl::Span<const uint8_t> buffer) {
499299
auto sapi_buffer = std::make_unique<sapi::v::Array<const uint8_t>>(
500300
buffer.data(), buffer.size());
501301
SAPI_RETURN_IF_ERROR(Allocate(sapi_buffer.get(), /*automatic_free=*/true));
502302
SAPI_RETURN_IF_ERROR(TransferToSandboxee(sapi_buffer.get()));
503303
return sapi_buffer;
504304
}
505305

506-
absl::StatusOr<std::string> Sandbox::GetCString(const v::RemotePtr& str,
507-
size_t max_length) {
306+
absl::StatusOr<std::string> SandboxBase::GetCString(const v::RemotePtr& str,
307+
size_t max_length) {
508308
if (!is_active()) {
509309
return absl::UnavailableError("Sandbox not active");
510310
}
@@ -532,20 +332,4 @@ absl::StatusOr<std::string> Sandbox::GetCString(const v::RemotePtr& str,
532332
return buffer;
533333
}
534334

535-
const sandbox2::Result& Sandbox::AwaitResult() {
536-
if (s2_ && !s2_awaited_) {
537-
result_ = s2_->AwaitResult();
538-
s2_awaited_ = true;
539-
}
540-
return result_;
541-
}
542-
543-
absl::Status Sandbox::SetWallTimeLimit(absl::Duration limit) const {
544-
if (!is_active()) {
545-
return absl::UnavailableError("Sandbox not active");
546-
}
547-
s2_->set_walltime_limit(limit);
548-
return absl::OkStatus();
549-
}
550-
551335
} // namespace sapi

0 commit comments

Comments
 (0)