Index: sandbox/linux/seccomp-bpf/sandbox_bpf.cc |
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc |
index 43c9af6f339be05c5a841da22474b3449144ab17..189485062b27ff2cef7b9ad8ee623ca3f93e1601 100644 |
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc |
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc |
@@ -42,6 +42,7 @@ |
#include "sandbox/linux/seccomp-bpf/verifier.h" |
#include "sandbox/linux/services/linux_syscalls.h" |
#include "sandbox/linux/services/syscall_wrappers.h" |
+#include "sandbox/linux/services/thread_helpers.h" |
using sandbox::bpf_dsl::Allow; |
using sandbox::bpf_dsl::Error; |
@@ -122,33 +123,19 @@ void TryVsyscallProcess(void) { |
} |
} |
-bool IsSingleThreaded(int proc_fd) { |
- if (proc_fd < 0) { |
- // Cannot determine whether program is single-threaded. Hope for |
- // the best... |
- return true; |
- } |
- |
- struct stat sb; |
- int task = -1; |
- if ((task = openat(proc_fd, "self/task", O_RDONLY | O_DIRECTORY)) < 0 || |
- fstat(task, &sb) != 0 || sb.st_nlink != 3 || IGNORE_EINTR(close(task))) { |
- if (task >= 0) { |
- if (IGNORE_EINTR(close(task))) { |
- } |
- } |
- return false; |
- } |
- return true; |
+bool IsSingleThreaded(int proc_task_fd) { |
+ return ThreadHelpers::IsSingleThreaded(proc_task_fd); |
} |
} // namespace |
SandboxBPF::SandboxBPF() |
- : quiet_(false), proc_fd_(-1), sandbox_has_started_(false), policy_() { |
+ : quiet_(false), proc_task_fd_(-1), sandbox_has_started_(false), policy_() { |
} |
SandboxBPF::~SandboxBPF() { |
+ if (proc_task_fd_ != -1) |
+ IGNORE_EINTR(close(proc_task_fd_)); |
} |
bool SandboxBPF::IsValidSyscallNumber(int sysnum) { |
@@ -175,7 +162,7 @@ bool SandboxBPF::RunFunctionInPolicy(void (*code_in_sandbox)(), |
// This code is using fork() and should only ever run single-threaded. |
// Most of the code below is "async-signal-safe" and only minor changes |
// would be needed to support threads. |
- DCHECK(IsSingleThreaded(proc_fd_)); |
+ DCHECK(IsSingleThreaded(proc_task_fd_)); |
pid_t pid = fork(); |
if (pid < 0) { |
// Die if we cannot fork(). We would probably fail a little later |
@@ -282,58 +269,28 @@ bool SandboxBPF::KernelSupportSeccompBPF() { |
} |
// static |
-SandboxBPF::SandboxStatus SandboxBPF::SupportsSeccompSandbox(int proc_fd) { |
- // It the sandbox is currently active, we clearly must have support for |
- // sandboxing. |
- if (status_ == STATUS_ENABLED) { |
- return status_; |
- } |
- |
- // Even if the sandbox was previously available, something might have |
- // changed in our run-time environment. Check one more time. |
- if (status_ == STATUS_AVAILABLE) { |
- if (!IsSingleThreaded(proc_fd)) { |
- status_ = STATUS_UNAVAILABLE; |
- } |
- return status_; |
- } |
- |
- if (status_ == STATUS_UNAVAILABLE && IsSingleThreaded(proc_fd)) { |
- // All state transitions resulting in STATUS_UNAVAILABLE are immediately |
- // preceded by STATUS_AVAILABLE. Furthermore, these transitions all |
- // happen, if and only if they are triggered by the process being multi- |
- // threaded. |
- // In other words, if a single-threaded process is currently in the |
- // STATUS_UNAVAILABLE state, it is safe to assume that sandboxing is |
- // actually available. |
- status_ = STATUS_AVAILABLE; |
+SandboxBPF::SandboxStatus SandboxBPF::SupportsSeccompSandbox() { |
+ if (status_ != STATUS_UNKNOWN) { |
return status_; |
} |
// If we have not previously checked for availability of the sandbox or if |
// we otherwise don't believe to have a good cached value, we have to |
// perform a thorough check now. |
- if (status_ == STATUS_UNKNOWN) { |
- // We create our own private copy of a "Sandbox" object. This ensures that |
- // the object does not have any policies configured, that might interfere |
- // with the tests done by "KernelSupportSeccompBPF()". |
- SandboxBPF sandbox; |
- |
- // By setting "quiet_ = true" we suppress messages for expected and benign |
- // failures (e.g. if the current kernel lacks support for BPF filters). |
- sandbox.quiet_ = true; |
- sandbox.set_proc_fd(proc_fd); |
- status_ = sandbox.KernelSupportSeccompBPF() ? STATUS_AVAILABLE |
- : STATUS_UNSUPPORTED; |
- |
- // As we are performing our tests from a child process, the run-time |
- // environment that is visible to the sandbox is always guaranteed to be |
- // single-threaded. Let's check here whether the caller is single- |
- // threaded. Otherwise, we mark the sandbox as temporarily unavailable. |
- if (status_ == STATUS_AVAILABLE && !IsSingleThreaded(proc_fd)) { |
- status_ = STATUS_UNAVAILABLE; |
- } |
- } |
+ |
+ // We create our own private copy of a "Sandbox" object. This ensures that |
+ // the object does not have any policies configured, that might interfere |
+ // with the tests done by "KernelSupportSeccompBPF()". |
+ SandboxBPF sandbox; |
+ |
+ // By setting "quiet_ = true" we suppress messages for expected and benign |
+ // failures (e.g. if the current kernel lacks support for BPF filters). |
+ // TODO(jln): use kernel API to check for seccomp support now that things |
+ // have stabilized. |
+ sandbox.quiet_ = true; |
+ status_ = |
+ sandbox.KernelSupportSeccompBPF() ? STATUS_AVAILABLE : STATUS_UNSUPPORTED; |
+ |
return status_; |
} |
@@ -355,7 +312,9 @@ SandboxBPF::SupportsSeccompThreadFilterSynchronization() { |
} |
} |
-void SandboxBPF::set_proc_fd(int proc_fd) { proc_fd_ = proc_fd; } |
+void SandboxBPF::set_proc_task_fd(int proc_task_fd) { |
+ proc_task_fd_ = proc_task_fd; |
+} |
bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) { |
CHECK(thread_state == PROCESS_SINGLE_THREADED || |
@@ -372,24 +331,17 @@ bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) { |
"object instead."); |
return false; |
} |
- if (proc_fd_ < 0) { |
- proc_fd_ = open("/proc", O_RDONLY | O_DIRECTORY); |
- } |
- if (proc_fd_ < 0) { |
- // For now, continue in degraded mode, if we can't access /proc. |
- // In the future, we might want to tighten this requirement. |
- } |
bool supports_tsync = |
SupportsSeccompThreadFilterSynchronization() == STATUS_AVAILABLE; |
if (thread_state == PROCESS_SINGLE_THREADED) { |
- if (!IsSingleThreaded(proc_fd_)) { |
+ if (!IsSingleThreaded(proc_task_fd_)) { |
SANDBOX_DIE("Cannot start sandbox; process is already multi-threaded"); |
return false; |
} |
} else if (thread_state == PROCESS_MULTI_THREADED) { |
- if (IsSingleThreaded(proc_fd_)) { |
+ if (IsSingleThreaded(proc_task_fd_)) { |
SANDBOX_DIE("Cannot start sandbox; " |
"process may be single-threaded when reported as not"); |
return false; |
@@ -404,12 +356,12 @@ bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) { |
// 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_fd_ >= 0) { |
- if (IGNORE_EINTR(close(proc_fd_))) { |
+ if (proc_task_fd_ >= 0) { |
+ if (IGNORE_EINTR(close(proc_task_fd_))) { |
SANDBOX_DIE("Failed to close file descriptor for /proc"); |
return false; |
} |
- proc_fd_ = -1; |
+ proc_task_fd_ = -1; |
} |
// Install the filters. |