Skip to content

Commit

Permalink
Add ForkClientContext holding a fork client and associated executor w…
Browse files Browse the repository at this point in the history
…hich can

be shared between mutilple Sandbox objects.

PiperOrigin-RevId: 631788087
Change-Id: I352f473929ff22db183979253645031bc0bf93f1
  • Loading branch information
Sandboxed API Team authored and Copybara-Service committed May 8, 2024
1 parent 69bc135 commit 3cdbf52
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 43 deletions.
97 changes: 60 additions & 37 deletions sandboxed_api/sandbox.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <utility>
#include <vector>

#include "sandboxed_api/file_toc.h"
#include "absl/base/dynamic_annotations.h"
#include "absl/base/macros.h"
#include "absl/log/log.h"
Expand All @@ -36,6 +37,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "sandboxed_api/call.h"
Expand All @@ -60,12 +62,26 @@

namespace sapi {

Sandbox::Sandbox(const FileToc* embed_lib_toc) {
owned_fork_client_context_ =
std::make_unique<ForkClientContext>(embed_lib_toc);
fork_client_context_ = owned_fork_client_context_.get();
}

Sandbox::Sandbox(std::nullptr_t)
: Sandbox(static_cast<const FileToc*>(nullptr)) {}

Sandbox::~Sandbox() {
Terminate();
// The forkserver will die automatically when the executor goes out of scope
// and closes the comms object.
}

void Sandbox::SetForkClientContext(ForkClientContext* fork_client_context) {
fork_client_context_ = fork_client_context;
owned_fork_client_context_.reset();
}

// A generic policy which should work with majority of typical libraries, which
// are single-threaded and require ~30 basic syscalls.
//
Expand Down Expand Up @@ -158,45 +174,52 @@ absl::Status Sandbox::Init(bool use_unotify_monitor) {
return absl::OkStatus();
}

// Initialize the forkserver if it is not already running.
if (!fork_client_) {
// If FileToc was specified, it will be used over any paths to the SAPI
// library.
std::string lib_path;
int embed_lib_fd = -1;
if (embed_lib_toc_ && !sapi::host_os::IsAndroid()) {
embed_lib_fd = EmbedFile::instance()->GetDupFdForFileToc(embed_lib_toc_);
if (embed_lib_fd == -1) {
PLOG(ERROR) << "Cannot create executable FD for TOC:'"
<< embed_lib_toc_->name << "'";
return absl::UnavailableError("Could not create executable FD");
sandbox2::ForkClient* fork_client;
{
absl::MutexLock lock(&fork_client_context_->mu_);
// Initialize the forkserver if it is not already running.
if (!fork_client_context_->client_) {
// If FileToc was specified, it will be used over any paths to the SAPI
// library.
std::string lib_path;
int embed_lib_fd = -1;
const FileToc* embed_lib_toc = fork_client_context_->embed_lib_toc_;
if (embed_lib_toc && !sapi::host_os::IsAndroid()) {
embed_lib_fd = EmbedFile::instance()->GetDupFdForFileToc(embed_lib_toc);
if (embed_lib_fd == -1) {
PLOG(ERROR) << "Cannot create executable FD for TOC:'"
<< embed_lib_toc->name << "'";
return absl::UnavailableError("Could not create executable FD");
}
lib_path = embed_lib_toc->name;
} else {
lib_path = PathToSAPILib(GetLibPath());
if (lib_path.empty()) {
LOG(ERROR) << "SAPI library path is empty";
return absl::FailedPreconditionError("No SAPI library path given");
}
}
lib_path = embed_lib_toc_->name;
} else {
lib_path = PathToSAPILib(GetLibPath());
if (lib_path.empty()) {
LOG(ERROR) << "SAPI library path is empty";
return absl::FailedPreconditionError("No SAPI library path given");
std::vector<std::string> args = {lib_path};
// Additional arguments, if needed.
GetArgs(&args);
std::vector<std::string> envs{};
// Additional envvars, if needed.
GetEnvs(&envs);

fork_client_context_->executor_ =
(embed_lib_fd >= 0)
? std::make_unique<sandbox2::Executor>(embed_lib_fd, args, envs)
: std::make_unique<sandbox2::Executor>(lib_path, args, envs);

fork_client_context_->client_ =
fork_client_context_->executor_->StartForkServer();

if (!fork_client_context_->client_) {
LOG(ERROR) << "Could not start forkserver";
return absl::UnavailableError("Could not start the forkserver");
}
}
std::vector<std::string> args = {lib_path};
// Additional arguments, if needed.
GetArgs(&args);
std::vector<std::string> envs{};
// Additional envvars, if needed.
GetEnvs(&envs);

forkserver_executor_ =
(embed_lib_fd >= 0)
? std::make_unique<sandbox2::Executor>(embed_lib_fd, args, envs)
: std::make_unique<sandbox2::Executor>(lib_path, args, envs);

fork_client_ = forkserver_executor_->StartForkServer();

if (!fork_client_) {
LOG(ERROR) << "Could not start forkserver";
return absl::UnavailableError("Could not start the forkserver");
}
fork_client = fork_client_context_->client_.get();
}

sandbox2::PolicyBuilder policy_builder;
Expand All @@ -207,7 +230,7 @@ absl::Status Sandbox::Init(bool use_unotify_monitor) {
auto s2p = ModifyPolicy(&policy_builder);

// Spawn new process from the forkserver.
auto executor = std::make_unique<sandbox2::Executor>(fork_client_.get());
auto executor = std::make_unique<sandbox2::Executor>(fork_client);

executor
// The client.cc code is capable of enabling sandboxing on its own.
Expand Down
38 changes: 32 additions & 6 deletions sandboxed_api/sandbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#ifndef SANDBOXED_API_SANDBOX_H_
#define SANDBOXED_API_SANDBOX_H_

#include <cstddef>
#include <cstdint>
#include <ctime>
#include <initializer_list>
Expand All @@ -23,11 +24,14 @@
#include <vector>

#include "sandboxed_api/file_toc.h"
#include "absl/base/attributes.h"
#include "absl/base/macros.h"
#include "absl/base/thread_annotations.h"
#include "absl/log/globals.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "sandboxed_api/config.h"
Expand All @@ -41,18 +45,40 @@

namespace sapi {

// Context holding, potentially shared, fork client.
class ForkClientContext {
public:
explicit ForkClientContext(const FileToc* embed_lib_toc)
: embed_lib_toc_(embed_lib_toc) {}

private:
friend class Sandbox;
const FileToc* embed_lib_toc_;
absl::Mutex mu_;
std::unique_ptr<sandbox2::ForkClient> client_ ABSL_GUARDED_BY(mu_);
std::unique_ptr<sandbox2::Executor> executor_ ABSL_GUARDED_BY(mu_);
};

// The Sandbox class represents the sandboxed library. It provides users with
// means to communicate with it (make function calls, transfer memory).
class Sandbox {
public:
explicit Sandbox(const FileToc* embed_lib_toc)
: embed_lib_toc_(embed_lib_toc) {}
explicit Sandbox(
ForkClientContext* fork_client_context ABSL_ATTRIBUTE_LIFETIME_BOUND)
: fork_client_context_(fork_client_context) {}

explicit Sandbox(const FileToc* embed_lib_toc ABSL_ATTRIBUTE_LIFETIME_BOUND);

explicit Sandbox(std::nullptr_t);

Sandbox(const Sandbox&) = delete;
Sandbox& operator=(const Sandbox&) = delete;

virtual ~Sandbox();

void SetForkClientContext(
ForkClientContext* fork_client_context ABSL_ATTRIBUTE_LIFETIME_BOUND);

// Initializes a new sandboxing session.
absl::Status Init(bool use_unotify_monitor = false);

Expand Down Expand Up @@ -179,10 +205,6 @@ class Sandbox {
// Exits the sandboxee.
void Exit() const;

// The client to the library forkserver.
std::unique_ptr<sandbox2::ForkClient> fork_client_;
std::unique_ptr<sandbox2::Executor> forkserver_executor_;

// The main sandbox2::Sandbox2 object.
std::unique_ptr<sandbox2::Sandbox2> s2_;
// Marks whether Sandbox2 result was already fetched.
Expand All @@ -203,6 +225,10 @@ class Sandbox {
// FileTOC with the embedded library, takes precedence over GetLibPath if
// present (not nullptr).
const FileToc* embed_lib_toc_;

ForkClientContext* fork_client_context_;
// Set if the object owns the client context instance.
std::unique_ptr<ForkClientContext> owned_fork_client_context_;
};

} // namespace sapi
Expand Down

0 comments on commit 3cdbf52

Please sign in to comment.