summaryrefslogtreecommitdiff
path: root/chromium/content/common/sandbox_linux/sandbox_linux.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/common/sandbox_linux/sandbox_linux.cc')
-rw-r--r--chromium/content/common/sandbox_linux/sandbox_linux.cc166
1 files changed, 107 insertions, 59 deletions
diff --git a/chromium/content/common/sandbox_linux/sandbox_linux.cc b/chromium/content/common/sandbox_linux/sandbox_linux.cc
index 12e11d8e131..b2a7b3e6e96 100644
--- a/chromium/content/common/sandbox_linux/sandbox_linux.cc
+++ b/chromium/content/common/sandbox_linux/sandbox_linux.cc
@@ -11,6 +11,8 @@
#include <unistd.h>
#include <limits>
+#include <string>
+#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
@@ -26,11 +28,15 @@
#include "base/sys_info.h"
#include "base/time/time.h"
#include "build/build_config.h"
+#include "content/common/sandbox_linux/sandbox_debug_handling_linux.h"
#include "content/common/sandbox_linux/sandbox_linux.h"
#include "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/sandbox_linux.h"
#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/services/namespace_sandbox.h"
+#include "sandbox/linux/services/proc_util.h"
+#include "sandbox/linux/services/resource_limits.h"
#include "sandbox/linux/services/thread_helpers.h"
#include "sandbox/linux/services/yama.h"
#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
@@ -62,19 +68,6 @@ void LogSandboxStarted(const std::string& sandbox_name) {
VLOG(1) << activated_sandbox;
}
-bool AddResourceLimit(int resource, rlim_t limit) {
- struct rlimit old_rlimit;
- if (getrlimit(resource, &old_rlimit))
- return false;
- // Make sure we don't raise the existing limit.
- const struct rlimit new_rlimit = {
- std::min(old_rlimit.rlim_cur, limit),
- std::min(old_rlimit.rlim_max, limit)
- };
- int rc = setrlimit(resource, &new_rlimit);
- return rc == 0;
-}
-
bool IsRunningTSAN() {
#if defined(THREAD_SANITIZER)
return true;
@@ -83,19 +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).
-int OpenProcTaskFd(int proc_fd) {
- int proc_self_task = -1;
+// 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.
+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 = openat(proc_fd, "self/task/", O_RDONLY | O_DIRECTORY);
+ 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 = open("/proc/self/task/", O_RDONLY | O_DIRECTORY);
+ 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
@@ -108,9 +105,10 @@ LinuxSandbox::LinuxSandbox()
sandbox_status_flags_(kSandboxLinuxInvalid),
pre_initialized_(false),
seccomp_bpf_supported_(false),
+ seccomp_bpf_with_tsync_supported_(false),
yama_is_enforcing_(false),
- setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create())
-{
+ initialize_sandbox_ran_(false),
+ setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) {
if (setuid_sandbox_client_ == NULL) {
LOG(FATAL) << "Failed to instantiate the setuid sandbox client.";
}
@@ -121,6 +119,9 @@ LinuxSandbox::LinuxSandbox()
}
LinuxSandbox::~LinuxSandbox() {
+ if (pre_initialized_) {
+ CHECK(initialize_sandbox_ran_);
+ }
}
LinuxSandbox* LinuxSandbox::GetInstance() {
@@ -144,12 +145,14 @@ void LinuxSandbox::PreinitializeSandbox() {
// its contents before the sandbox is enabled. It also pre-opens the
// object files that are already loaded in the process address space.
base::debug::EnableInProcessStackDumpingForSandbox();
+#endif // !defined(NDEBUG)
- // Open proc_fd_ only in Debug mode so that forgetting to close it doesn't
- // produce a sandbox escape in Release mode.
- proc_fd_ = open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+ // Open proc_fd_. It would break the security of the setuid sandbox if it was
+ // not closed.
+ // If LinuxSandbox::PreinitializeSandbox() runs, InitializeSandbox() must run
+ // as well.
+ proc_fd_ = HANDLE_EINTR(open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC));
CHECK_GE(proc_fd_, 0);
-#endif // !defined(NDEBUG)
// We "pre-warm" the code that detects supports for seccomp BPF.
if (SandboxSeccompBPF::IsSeccompBPFDesired()) {
if (!SandboxSeccompBPF::SupportsSandbox()) {
@@ -157,6 +160,10 @@ void LinuxSandbox::PreinitializeSandbox() {
} else {
seccomp_bpf_supported_ = true;
}
+
+ if (SandboxSeccompBPF::SupportsSandboxWithTsync()) {
+ seccomp_bpf_with_tsync_supported_ = true;
+ }
}
// Yama is a "global", system-level status. We assume it will not regress
@@ -167,6 +174,38 @@ void LinuxSandbox::PreinitializeSandbox() {
pre_initialized_ = true;
}
+void LinuxSandbox::EngageNamespaceSandbox() {
+ CHECK(pre_initialized_);
+ // Check being in a new PID namespace created by the namespace sandbox and
+ // being the init process.
+ CHECK(sandbox::NamespaceSandbox::InNewPidNamespace());
+ const pid_t pid = getpid();
+ CHECK_EQ(1, pid);
+
+ 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(sandbox::Credentials::DropFileSystemAccess(proc_fd_));
+
+ // We do not drop CAP_SYS_ADMIN because we need it to place each child process
+ // in its own PID namespace later on.
+ std::vector<sandbox::Credentials::Capability> caps;
+ caps.push_back(sandbox::Credentials::Capability::SYS_ADMIN);
+ CHECK(sandbox::Credentials::SetCapabilities(proc_fd_, caps));
+
+ // This needs to happen after moving to a new user NS, since doing so involves
+ // writing the UID/GID map.
+ CHECK(SandboxDebugHandling::SetDumpableStatusAndHandlers());
+}
+
+std::vector<int> LinuxSandbox::GetFileDescriptorsToClose() {
+ std::vector<int> fds;
+ if (proc_fd_ >= 0) {
+ fds.push_back(proc_fd_);
+ }
+ return fds;
+}
+
bool LinuxSandbox::InitializeSandbox() {
LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance();
return linux_sandbox->InitializeSandboxImpl();
@@ -178,7 +217,9 @@ void LinuxSandbox::StopThread(base::Thread* thread) {
}
int LinuxSandbox::GetStatus() {
- CHECK(pre_initialized_);
+ if (!pre_initialized_) {
+ return 0;
+ }
if (kSandboxLinuxInvalid == sandbox_status_flags_) {
// Initialize sandbox_status_flags_.
sandbox_status_flags_ = 0;
@@ -188,6 +229,12 @@ int LinuxSandbox::GetStatus() {
sandbox_status_flags_ |= kSandboxLinuxPIDNS;
if (setuid_sandbox_client_->IsInNewNETNamespace())
sandbox_status_flags_ |= kSandboxLinuxNetNS;
+ } else if (sandbox::NamespaceSandbox::InNewUserNamespace()) {
+ sandbox_status_flags_ |= kSandboxLinuxUserNS;
+ if (sandbox::NamespaceSandbox::InNewPidNamespace())
+ sandbox_status_flags_ |= kSandboxLinuxPIDNS;
+ if (sandbox::NamespaceSandbox::InNewNetNamespace())
+ sandbox_status_flags_ |= kSandboxLinuxNetNS;
}
// We report whether the sandbox will be activated when renderers, workers
@@ -197,6 +244,11 @@ int LinuxSandbox::GetStatus() {
sandbox_status_flags_ |= kSandboxLinuxSeccompBPF;
}
+ if (seccomp_bpf_with_tsync_supported() &&
+ SandboxSeccompBPF::ShouldEnableSeccompBPF(switches::kRendererProcess)) {
+ sandbox_status_flags_ |= kSandboxLinuxSeccompTSYNC;
+ }
+
if (yama_is_enforcing_) {
sandbox_status_flags_ |= kSandboxLinuxYama;
}
@@ -209,27 +261,13 @@ int LinuxSandbox::GetStatus() {
// PID namespaces and existing sandboxes, so "self" must really be used instead
// of using the pid.
bool LinuxSandbox::IsSingleThreaded() const {
- bool is_single_threaded = false;
- base::ScopedFD proc_self_task(OpenProcTaskFd(proc_fd_));
+ base::ScopedFD proc_fd(OpenProc(proc_fd_));
-// In Debug mode, it's mandatory to be able to count threads to catch bugs.
-#if !defined(NDEBUG)
- // Using CHECK here since we want to check all the cases where
- // !defined(NDEBUG)
- // gets built.
- CHECK(proc_self_task.is_valid())
- << "Could not count threads, the sandbox was not "
- << "pre-initialized properly.";
-#endif // !defined(NDEBUG)
+ CHECK(proc_fd.is_valid()) << "Could not count threads, the sandbox was not "
+ << "pre-initialized properly.";
- if (!proc_self_task.is_valid()) {
- // Pretend to be monothreaded if it can't be determined (for instance the
- // setuid sandbox is already engaged but no proc_fd_ is available).
- is_single_threaded = true;
- } else {
- is_single_threaded =
- sandbox::ThreadHelpers::IsSingleThreaded(proc_self_task.get());
- }
+ const bool is_single_threaded =
+ sandbox::ThreadHelpers::IsSingleThreaded(proc_fd.get());
return is_single_threaded;
}
@@ -247,16 +285,22 @@ sandbox::SetuidSandboxClient*
bool LinuxSandbox::StartSeccompBPF(const std::string& process_type) {
CHECK(!seccomp_bpf_started_);
CHECK(pre_initialized_);
- if (seccomp_bpf_supported())
- seccomp_bpf_started_ = SandboxSeccompBPF::StartSandbox(process_type);
+ if (seccomp_bpf_supported()) {
+ seccomp_bpf_started_ =
+ SandboxSeccompBPF::StartSandbox(process_type, OpenProc(proc_fd_));
+ }
- if (seccomp_bpf_started_)
+ if (seccomp_bpf_started_) {
LogSandboxStarted("seccomp-bpf");
+ }
return seccomp_bpf_started_;
}
bool LinuxSandbox::InitializeSandboxImpl() {
+ DCHECK(!initialize_sandbox_ran_);
+ initialize_sandbox_ran_ = true;
+
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
const std::string process_type =
command_line->GetSwitchValueASCII(switches::kProcessType);
@@ -329,6 +373,11 @@ bool LinuxSandbox::seccomp_bpf_supported() const {
return seccomp_bpf_supported_;
}
+bool LinuxSandbox::seccomp_bpf_with_tsync_supported() const {
+ CHECK(pre_initialized_);
+ return seccomp_bpf_with_tsync_supported_;
+}
+
bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) {
(void) process_type;
#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \
@@ -361,17 +410,16 @@ bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) {
// allocations that can't be index by an int.
const rlim_t kNewDataSegmentMaxSize = std::numeric_limits<int>::max();
- bool limited_as = AddResourceLimit(RLIMIT_AS, address_space_limit);
- bool limited_data = AddResourceLimit(RLIMIT_DATA, kNewDataSegmentMaxSize);
+ bool limited_as =
+ sandbox::ResourceLimits::Lower(RLIMIT_AS, address_space_limit);
+ bool limited_data =
+ sandbox::ResourceLimits::Lower(RLIMIT_DATA, kNewDataSegmentMaxSize);
// Cache the resource limit before turning on the sandbox.
base::SysInfo::AmountOfVirtualMemory();
return limited_as && limited_data;
#else
- // Silence the compiler warning about unused function. This doesn't actually
- // call AddResourceLimit().
- ignore_result(AddResourceLimit);
base::SysInfo::AmountOfVirtualMemory();
return false;
#endif // !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) &&
@@ -379,7 +427,7 @@ bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) {
}
bool LinuxSandbox::HasOpenDirectories() const {
- return sandbox::Credentials().HasOpenDirectory(proc_fd_);
+ return sandbox::ProcUtil::HasOpenDirectory(proc_fd_);
}
void LinuxSandbox::SealSandbox() {
@@ -406,10 +454,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