Skip to content

Commit

Permalink
Linux sandbox: better APIs with /proc/ arguments
Browse files Browse the repository at this point in the history
Unify sandbox:: APIs to always take /proc/ file descriptors
instead of /proc/self/ or /proc/self/task/.

Moreover, require |proc_fd| arguments to critical APIs rather
than rely on the caller to perform the right checks.

A descriptor to /proc is a better choice than a descriptor to
/proc/self/* because it keeps the same semantics after a fork().

BUG=312380, 457377
TBR=nasko

Review URL: https://codereview.chromium.org/938223004

Cr-Commit-Position: refs/heads/master@{#317757}
  • Loading branch information
jln authored and Commit bot committed Feb 24, 2015
1 parent 7396d54 commit 4d91216
Show file tree
Hide file tree
Showing 23 changed files with 188 additions and 187 deletions.
4 changes: 2 additions & 2 deletions components/nacl/loader/nonsfi/nonsfi_sandbox.cc
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,11 @@ ResultExpr NaClNonSfiBPFSandboxPolicy::InvalidSyscall() const {
return CrashSIGSYS();
}

bool InitializeBPFSandbox(base::ScopedFD proc_task_fd) {
bool InitializeBPFSandbox(base::ScopedFD proc_fd) {
bool sandbox_is_initialized = content::InitializeSandbox(
scoped_ptr<sandbox::bpf_dsl::Policy>(
new nacl::nonsfi::NaClNonSfiBPFSandboxPolicy()),
proc_task_fd.Pass());
proc_fd.Pass());
if (!sandbox_is_initialized)
return false;
RunSandboxSanityChecks();
Expand Down
2 changes: 1 addition & 1 deletion components/nacl/loader/nonsfi/nonsfi_sandbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class NaClNonSfiBPFSandboxPolicy : public sandbox::bpf_dsl::Policy {

// Initializes seccomp-bpf sandbox for non-SFI NaCl. Returns false on
// failure.
bool InitializeBPFSandbox(base::ScopedFD proc_task_fd);
bool InitializeBPFSandbox(base::ScopedFD proc_fd);

} // namespace nonsfi
} // namespace nacl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@ void RunSandboxSanityChecks() {

#endif // defined(USE_SECCOMP_BPF)

bool InitializeBPFSandbox(base::ScopedFD proc_task_fd) {
bool InitializeBPFSandbox(base::ScopedFD proc_fd) {
#if defined(USE_SECCOMP_BPF)
bool sandbox_is_initialized = content::InitializeSandbox(
scoped_ptr<sandbox::bpf_dsl::Policy>(new NaClBPFSandboxPolicy),
proc_task_fd.Pass());
proc_fd.Pass());
if (sandbox_is_initialized) {
RunSandboxSanityChecks();
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace nacl {

bool InitializeBPFSandbox(base::ScopedFD proc_task_fd);
bool InitializeBPFSandbox(base::ScopedFD proc_fd);

} // namespace nacl

Expand Down
33 changes: 11 additions & 22 deletions components/nacl/loader/sandbox_linux/nacl_sandbox_linux.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,6 @@ bool IsSandboxed() {
return true;
}

// Open a new file descriptor to /proc/self/task/ by using
// |proc_fd|.
base::ScopedFD GetProcSelfTask(int proc_fd) {
base::ScopedFD proc_self_task(HANDLE_EINTR(
openat(proc_fd, "self/task/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)));
PCHECK(proc_self_task.is_valid());
return proc_self_task.Pass();
}

bool MaybeSetProcessNonDumpable() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
Expand Down Expand Up @@ -122,8 +113,7 @@ NaClSandbox::~NaClSandbox() {

bool NaClSandbox::IsSingleThreaded() {
CHECK(proc_fd_.is_valid());
base::ScopedFD proc_self_task(GetProcSelfTask(proc_fd_.get()));
return sandbox::ThreadHelpers::IsSingleThreaded(proc_self_task.get());
return sandbox::ThreadHelpers::IsSingleThreaded(proc_fd_.get());
}

bool NaClSandbox::HasOpenDirectory() {
Expand All @@ -149,11 +139,10 @@ void NaClSandbox::InitializeLayerOneSandbox() {
layer_one_enabled_ = true;
} else if (sandbox::NamespaceSandbox::InNewUserNamespace()) {
CHECK(sandbox::Credentials::MoveToNewUserNS());
// This relies on SealLayerOneSandbox() to be called later.
CHECK(!HasOpenDirectory());
CHECK(sandbox::Credentials::DropFileSystemAccess());
CHECK(IsSingleThreaded());
CHECK(sandbox::Credentials::DropAllCapabilities());
// This relies on SealLayerOneSandbox() to be called later since this
// class is keeping a file descriptor to /proc/.
CHECK(sandbox::Credentials::DropFileSystemAccess(proc_fd_.get()));
CHECK(sandbox::Credentials::DropAllCapabilities(proc_fd_.get()));
CHECK(IsSandboxed());
layer_one_enabled_ = true;
}
Expand Down Expand Up @@ -189,19 +178,19 @@ void NaClSandbox::InitializeLayerTwoSandbox(bool uses_nonsfi_mode) {

RestrictAddressSpaceUsage();

base::ScopedFD proc_self_task(GetProcSelfTask(proc_fd_.get()));

// Pass proc_fd_ ownership to the BPF sandbox, which guarantees it will
// be closed. There is no point in keeping it around since the BPF policy
// will prevent its usage.
if (uses_nonsfi_mode) {
layer_two_enabled_ =
nacl::nonsfi::InitializeBPFSandbox(proc_self_task.Pass());
layer_two_enabled_ = nacl::nonsfi::InitializeBPFSandbox(proc_fd_.Pass());
layer_two_is_nonsfi_ = true;
} else {
layer_two_enabled_ = nacl::InitializeBPFSandbox(proc_self_task.Pass());
layer_two_enabled_ = nacl::InitializeBPFSandbox(proc_fd_.Pass());
}
}

void NaClSandbox::SealLayerOneSandbox() {
if (!layer_two_enabled_) {
if (proc_fd_.is_valid() && !layer_two_enabled_) {
// If nothing prevents us, check that there is no superfluous directory
// open.
CHECK(!HasOpenDirectory());
Expand Down
4 changes: 2 additions & 2 deletions content/common/sandbox_linux/sandbox_init_linux.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
namespace content {

bool InitializeSandbox(scoped_ptr<sandbox::bpf_dsl::Policy> policy,
base::ScopedFD proc_task_fd) {
base::ScopedFD proc_fd) {
return SandboxSeccompBPF::StartSandboxWithExternalPolicy(policy.Pass(),
proc_task_fd.Pass());
proc_fd.Pass());
}

scoped_ptr<sandbox::bpf_dsl::Policy> GetBPFSandboxBaselinePolicy() {
Expand Down
47 changes: 22 additions & 25 deletions content/common/sandbox_linux/sandbox_linux.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,22 +76,23 @@ bool IsRunningTSAN() {
#endif
}

// Try to open /proc/self/task/ with the help of |proc_fd|. |proc_fd| can be
// -1. Will return -1 on error and set errno like open(2).
// Get a file descriptor to /proc. Either duplicate |proc_fd| or try to open
// it by using the filesystem directly.
// TODO(jln): get rid of this ugly interface.
int OpenProcTaskFd(int proc_fd) {
int proc_self_task = -1;
base::ScopedFD OpenProc(int proc_fd) {
int ret_proc_fd = -1;
if (proc_fd >= 0) {
// If a handle to /proc is available, use it. This allows to bypass file
// system restrictions.
proc_self_task = HANDLE_EINTR(
openat(proc_fd, "self/task/", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
ret_proc_fd =
HANDLE_EINTR(openat(proc_fd, ".", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
} else {
// Otherwise, make an attempt to access the file system directly.
proc_self_task = HANDLE_EINTR(openat(AT_FDCWD, "/proc/self/task/",
O_RDONLY | O_DIRECTORY | O_CLOEXEC));
ret_proc_fd = HANDLE_EINTR(
openat(AT_FDCWD, "/proc/", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
}
return proc_self_task;
DCHECK_LE(0, ret_proc_fd);
return base::ScopedFD(ret_proc_fd);
}

} // namespace
Expand Down Expand Up @@ -183,11 +184,9 @@ void LinuxSandbox::EngageNamespaceSandbox() {

CHECK(sandbox::Credentials::MoveToNewUserNS());
// Note: this requires SealSandbox() to be called later in this process to be
// safe, as this class is keeping a file descriptor to /proc.
CHECK(!HasOpenDirectories());
CHECK(sandbox::Credentials::DropFileSystemAccess());
CHECK(IsSingleThreaded());
CHECK(sandbox::Credentials::DropAllCapabilities());
// safe, as this class is keeping a file descriptor to /proc/.
CHECK(sandbox::Credentials::DropFileSystemAccess(proc_fd_));
CHECK(sandbox::Credentials::DropAllCapabilities(proc_fd_));

// This needs to happen after moving to a new user NS, since doing so involves
// writing the UID/GID map.
Expand Down Expand Up @@ -257,14 +256,13 @@ int LinuxSandbox::GetStatus() {
// PID namespaces and existing sandboxes, so "self" must really be used instead
// of using the pid.
bool LinuxSandbox::IsSingleThreaded() const {
base::ScopedFD proc_self_task(OpenProcTaskFd(proc_fd_));
base::ScopedFD proc_fd(OpenProc(proc_fd_));

CHECK(proc_self_task.is_valid())
<< "Could not count threads, the sandbox was not "
<< "pre-initialized properly.";
CHECK(proc_fd.is_valid()) << "Could not count threads, the sandbox was not "
<< "pre-initialized properly.";

const bool is_single_threaded =
sandbox::ThreadHelpers::IsSingleThreaded(proc_self_task.get());
sandbox::ThreadHelpers::IsSingleThreaded(proc_fd.get());

return is_single_threaded;
}
Expand All @@ -283,9 +281,8 @@ bool LinuxSandbox::StartSeccompBPF(const std::string& process_type) {
CHECK(!seccomp_bpf_started_);
CHECK(pre_initialized_);
if (seccomp_bpf_supported()) {
base::ScopedFD proc_self_task(OpenProcTaskFd(proc_fd_));
seccomp_bpf_started_ =
SandboxSeccompBPF::StartSandbox(process_type, proc_self_task.Pass());
SandboxSeccompBPF::StartSandbox(process_type, OpenProc(proc_fd_));
}

if (seccomp_bpf_started_) {
Expand Down Expand Up @@ -452,10 +449,10 @@ void LinuxSandbox::CheckForBrokenPromises(const std::string& process_type) {

void LinuxSandbox::StopThreadAndEnsureNotCounted(base::Thread* thread) const {
DCHECK(thread);
base::ScopedFD proc_self_task(OpenProcTaskFd(proc_fd_));
PCHECK(proc_self_task.is_valid());
CHECK(sandbox::ThreadHelpers::StopThreadAndWatchProcFS(proc_self_task.get(),
thread));
base::ScopedFD proc_fd(OpenProc(proc_fd_));
PCHECK(proc_fd.is_valid());
CHECK(
sandbox::ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.get(), thread));
}

} // namespace content
18 changes: 9 additions & 9 deletions content/common/sandbox_linux/sandbox_seccomp_bpf_linux.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ namespace content {
namespace {

void StartSandboxWithPolicy(sandbox::bpf_dsl::Policy* policy,
base::ScopedFD proc_task_fd);
base::ScopedFD proc_fd);

inline bool IsChromeOS() {
#if defined(OS_CHROMEOS)
Expand Down Expand Up @@ -148,15 +148,15 @@ void RunSandboxSanityChecks(const std::string& process_type) {

// This function takes ownership of |policy|.
void StartSandboxWithPolicy(sandbox::bpf_dsl::Policy* policy,
base::ScopedFD proc_task_fd) {
base::ScopedFD proc_fd) {
// Starting the sandbox is a one-way operation. The kernel doesn't allow
// us to unload a sandbox policy after it has been started. Nonetheless,
// in order to make the use of the "Sandbox" object easier, we allow for
// the object to be destroyed after the sandbox has been started. Note that
// doing so does not stop the sandbox.
SandboxBPF sandbox(policy);

sandbox.SetProcTaskFd(proc_task_fd.Pass());
sandbox.SetProcFd(proc_fd.Pass());
CHECK(sandbox.StartSandbox(SandboxBPF::SeccompLevel::SINGLE_THREADED));
}

Expand Down Expand Up @@ -187,7 +187,7 @@ scoped_ptr<SandboxBPFBasePolicy> GetGpuProcessSandbox() {
// Initialize the seccomp-bpf sandbox.
bool StartBPFSandbox(const base::CommandLine& command_line,
const std::string& process_type,
base::ScopedFD proc_task_fd) {
base::ScopedFD proc_fd) {
scoped_ptr<SandboxBPFBasePolicy> policy;

if (process_type == switches::kGpuProcess) {
Expand All @@ -204,7 +204,7 @@ bool StartBPFSandbox(const base::CommandLine& command_line,
}

CHECK(policy->PreSandboxHook());
StartSandboxWithPolicy(policy.release(), proc_task_fd.Pass());
StartSandboxWithPolicy(policy.release(), proc_fd.Pass());

RunSandboxSanityChecks(process_type);
return true;
Expand Down Expand Up @@ -267,7 +267,7 @@ bool SandboxSeccompBPF::SupportsSandboxWithTsync() {
}

bool SandboxSeccompBPF::StartSandbox(const std::string& process_type,
base::ScopedFD proc_task_fd) {
base::ScopedFD proc_fd) {
#if defined(USE_SECCOMP_BPF)
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
Expand All @@ -278,7 +278,7 @@ bool SandboxSeccompBPF::StartSandbox(const std::string& process_type,
// If the kernel supports the sandbox, and if the command line says we
// should enable it, enable it or die.
bool started_sandbox =
StartBPFSandbox(command_line, process_type, proc_task_fd.Pass());
StartBPFSandbox(command_line, process_type, proc_fd.Pass());
CHECK(started_sandbox);
return true;
}
Expand All @@ -288,11 +288,11 @@ bool SandboxSeccompBPF::StartSandbox(const std::string& process_type,

bool SandboxSeccompBPF::StartSandboxWithExternalPolicy(
scoped_ptr<sandbox::bpf_dsl::Policy> policy,
base::ScopedFD proc_task_fd) {
base::ScopedFD proc_fd) {
#if defined(USE_SECCOMP_BPF)
if (IsSeccompBPFDesired() && SupportsSandbox()) {
CHECK(policy);
StartSandboxWithPolicy(policy.release(), proc_task_fd.Pass());
StartSandboxWithPolicy(policy.release(), proc_fd.Pass());
return true;
}
#endif // defined(USE_SECCOMP_BPF)
Expand Down
4 changes: 2 additions & 2 deletions content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ class SandboxSeccompBPF {
// Start the sandbox and apply the policy for process_type, depending on
// command line switches.
static bool StartSandbox(const std::string& process_type,
base::ScopedFD proc_task_fd);
base::ScopedFD proc_fd);

// This is the API to enable a seccomp-bpf sandbox by using an
// external policy.
static bool StartSandboxWithExternalPolicy(
scoped_ptr<sandbox::bpf_dsl::Policy> policy,
base::ScopedFD proc_task_fd);
base::ScopedFD proc_fd);
// The "baseline" policy can be a useful base to build a sandbox policy.
static scoped_ptr<sandbox::bpf_dsl::Policy> GetBaselinePolicy();

Expand Down
4 changes: 2 additions & 2 deletions content/public/common/sandbox_init.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,11 @@ class SandboxInitializerDelegate;

// Initialize a seccomp-bpf sandbox. |policy| may not be NULL.
// If an existing layer of sandboxing is present that would prevent access to
// /proc, |proc_task_fd| must be a valid file descriptor to /proc/self/task.
// /proc, |proc_fd| must be a valid file descriptor to /proc/.
// Returns true if the sandbox has been properly engaged.
CONTENT_EXPORT bool InitializeSandbox(
scoped_ptr<sandbox::bpf_dsl::Policy> policy,
base::ScopedFD proc_task_fd);
base::ScopedFD proc_fd);

// Return a "baseline" policy. This is used by a SandboxInitializerDelegate to
// implement a policy that is derived from the baseline.
Expand Down
22 changes: 11 additions & 11 deletions sandbox/linux/seccomp-bpf/sandbox_bpf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ namespace {

bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }

bool IsSingleThreaded(int proc_task_fd) {
return ThreadHelpers::IsSingleThreaded(proc_task_fd);
bool IsSingleThreaded(int proc_fd) {
return ThreadHelpers::IsSingleThreaded(proc_fd);
}

// Check if the kernel supports seccomp-filter (a.k.a. seccomp mode 2) via
Expand Down Expand Up @@ -82,7 +82,7 @@ bool KernelSupportsSeccompTsync() {
} // namespace

SandboxBPF::SandboxBPF(bpf_dsl::Policy* policy)
: proc_task_fd_(), sandbox_has_started_(false), policy_(policy) {
: proc_fd_(), sandbox_has_started_(false), policy_(policy) {
}

SandboxBPF::~SandboxBPF() {
Expand Down Expand Up @@ -118,18 +118,18 @@ bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level) {
return false;
}

if (!proc_task_fd_.is_valid()) {
SetProcTaskFd(ProcUtil::OpenProcSelfTask());
if (!proc_fd_.is_valid()) {
SetProcFd(ProcUtil::OpenProc());
}

const bool supports_tsync = KernelSupportsSeccompTsync();

if (seccomp_level == SeccompLevel::SINGLE_THREADED) {
// Wait for /proc/self/task/ to update if needed and assert the
// process is single threaded.
ThreadHelpers::AssertSingleThreaded(proc_task_fd_.get());
ThreadHelpers::AssertSingleThreaded(proc_fd_.get());
} else if (seccomp_level == SeccompLevel::MULTI_THREADED) {
if (IsSingleThreaded(proc_task_fd_.get())) {
if (IsSingleThreaded(proc_fd_.get())) {
SANDBOX_DIE("Cannot start sandbox; "
"process may be single-threaded when reported as not");
return false;
Expand All @@ -144,8 +144,8 @@ bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level) {
// We no longer need access to any files in /proc. We want to do this
// before installing the filters, just in case that our policy denies
// close().
if (proc_task_fd_.is_valid()) {
proc_task_fd_.reset();
if (proc_fd_.is_valid()) {
proc_fd_.reset();
}

// Install the filters.
Expand All @@ -155,8 +155,8 @@ bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level) {
return true;
}

void SandboxBPF::SetProcTaskFd(base::ScopedFD proc_task_fd) {
proc_task_fd_.swap(proc_task_fd);
void SandboxBPF::SetProcFd(base::ScopedFD proc_fd) {
proc_fd_.swap(proc_fd);
}

// static
Expand Down
Loading

0 comments on commit 4d91216

Please sign in to comment.