Fix experimental app list search box disappearing on profile switch.
[chromium-blink-merge.git] / components / nacl / loader / sandbox_linux / nacl_sandbox_linux.cc
blobdfe177c8a0e02d89af96a4c2baf83f71287a30cd
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/nacl/loader/sandbox_linux/nacl_sandbox_linux.h"
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <sys/prctl.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <unistd.h>
14 #include <limits>
16 #include "base/basictypes.h"
17 #include "base/callback.h"
18 #include "base/command_line.h"
19 #include "base/compiler_specific.h"
20 #include "base/files/scoped_file.h"
21 #include "base/logging.h"
22 #include "base/memory/scoped_ptr.h"
23 #include "base/posix/eintr_wrapper.h"
24 #include "build/build_config.h"
25 #include "components/nacl/common/nacl_switches.h"
26 #include "components/nacl/loader/nonsfi/nonsfi_sandbox.h"
27 #include "components/nacl/loader/sandbox_linux/nacl_bpf_sandbox_linux.h"
28 #include "content/public/common/content_switches.h"
29 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
30 #include "sandbox/linux/services/credentials.h"
31 #include "sandbox/linux/services/namespace_sandbox.h"
32 #include "sandbox/linux/services/proc_util.h"
33 #include "sandbox/linux/services/resource_limits.h"
34 #include "sandbox/linux/services/thread_helpers.h"
35 #include "sandbox/linux/suid/client/setuid_sandbox_client.h"
37 namespace nacl {
39 namespace {
41 // This is a poor man's check on whether we are sandboxed.
42 bool IsSandboxed() {
43 int proc_fd = open("/proc/self/exe", O_RDONLY);
44 if (proc_fd >= 0) {
45 PCHECK(0 == IGNORE_EINTR(close(proc_fd)));
46 return false;
48 return true;
51 // Open a new file descriptor to /proc/self/task/ by using
52 // |proc_fd|.
53 base::ScopedFD GetProcSelfTask(int proc_fd) {
54 base::ScopedFD proc_self_task(HANDLE_EINTR(
55 openat(proc_fd, "self/task/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)));
56 PCHECK(proc_self_task.is_valid());
57 return proc_self_task.Pass();
60 bool MaybeSetProcessNonDumpable() {
61 const base::CommandLine& command_line =
62 *base::CommandLine::ForCurrentProcess();
63 if (command_line.HasSwitch(switches::kAllowSandboxDebugging)) {
64 return true;
67 if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) != 0) {
68 PLOG(ERROR) << "Failed to set non-dumpable flag";
69 return false;
72 return prctl(PR_GET_DUMPABLE) == 0;
75 void RestrictAddressSpaceUsage() {
76 #if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
77 defined(THREAD_SANITIZER)
78 // Sanitizers need to reserve huge chunks of the address space.
79 return;
80 #endif
82 // Add a limit to the brk() heap that would prevent allocations that can't be
83 // indexed by an int. This helps working around typical security bugs.
84 // This could almost certainly be set to zero. GLibc's allocator and others
85 // would fall-back to mmap if brk() fails.
86 const rlim_t kNewDataSegmentMaxSize = std::numeric_limits<int>::max();
87 CHECK(sandbox::ResourceLimits::Lower(RLIMIT_DATA, kNewDataSegmentMaxSize));
89 #if defined(ARCH_CPU_64_BITS)
90 // NaCl's x86-64 sandbox allocated 88GB address of space during startup:
91 // - The main sandbox is 4GB
92 // - There are two guard regions of 40GB each.
93 // - 4GB are allocated extra to have a 4GB-aligned address.
94 // See https://crbug.com/455839
96 // Set the limit to 128 GB and have some margin.
97 const rlim_t kNewAddressSpaceLimit = 1UL << 37;
98 #else
99 // Some architectures such as X86 allow 32 bits processes to switch to 64
100 // bits when running under 64 bits kernels. Set a limit in case this happens.
101 const rlim_t kNewAddressSpaceLimit = std::numeric_limits<uint32_t>::max();
102 #endif
103 CHECK(sandbox::ResourceLimits::Lower(RLIMIT_AS, kNewAddressSpaceLimit));
106 } // namespace
108 NaClSandbox::NaClSandbox()
109 : layer_one_enabled_(false),
110 layer_one_sealed_(false),
111 layer_two_enabled_(false),
112 layer_two_is_nonsfi_(false),
113 proc_fd_(-1),
114 setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) {
115 proc_fd_.reset(
116 HANDLE_EINTR(open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC)));
117 PCHECK(proc_fd_.is_valid());
120 NaClSandbox::~NaClSandbox() {
123 bool NaClSandbox::IsSingleThreaded() {
124 CHECK(proc_fd_.is_valid());
125 base::ScopedFD proc_self_task(GetProcSelfTask(proc_fd_.get()));
126 return sandbox::ThreadHelpers::IsSingleThreaded(proc_self_task.get());
129 bool NaClSandbox::HasOpenDirectory() {
130 CHECK(proc_fd_.is_valid());
131 return sandbox::ProcUtil::HasOpenDirectory(proc_fd_.get());
134 void NaClSandbox::InitializeLayerOneSandbox() {
135 // Check that IsSandboxed() works. We should not be sandboxed at this point.
136 CHECK(!IsSandboxed()) << "Unexpectedly sandboxed!";
138 if (setuid_sandbox_client_->IsSuidSandboxChild()) {
139 setuid_sandbox_client_->CloseDummyFile();
141 // Make sure that no directory file descriptor is open, as it would bypass
142 // the setuid sandbox model.
143 CHECK(!HasOpenDirectory());
145 // Get sandboxed.
146 CHECK(setuid_sandbox_client_->ChrootMe());
147 CHECK(MaybeSetProcessNonDumpable());
148 CHECK(IsSandboxed());
149 layer_one_enabled_ = true;
150 } else if (sandbox::NamespaceSandbox::InNewUserNamespace()) {
151 CHECK(sandbox::Credentials::MoveToNewUserNS());
152 // This relies on SealLayerOneSandbox() to be called later.
153 CHECK(!HasOpenDirectory());
154 CHECK(sandbox::Credentials::DropFileSystemAccess());
155 CHECK(IsSingleThreaded());
156 CHECK(sandbox::Credentials::DropAllCapabilities());
157 CHECK(IsSandboxed());
158 layer_one_enabled_ = true;
162 void NaClSandbox::CheckForExpectedNumberOfOpenFds() {
163 // We expect to have the following FDs open:
164 // 1-3) stdin, stdout, stderr.
165 // 4) The /dev/urandom FD used by base::GetUrandomFD().
166 // 5) A dummy pipe FD used to overwrite kSandboxIPCChannel.
167 // 6) The socket for the Chrome IPC channel that's connected to the
168 // browser process, kPrimaryIPCChannel.
169 // We also have an fd for /proc (proc_fd_), but CountOpenFds excludes this.
171 // This sanity check ensures that dynamically loaded libraries don't
172 // leave any FDs open before we enable the sandbox.
173 int expected_num_fds = 6;
174 if (setuid_sandbox_client_->IsSuidSandboxChild()) {
175 // When using the setuid sandbox, there is one additional socket used for
176 // ChrootMe(). After ChrootMe(), it is no longer connected to anything.
177 ++expected_num_fds;
180 CHECK_EQ(expected_num_fds, sandbox::ProcUtil::CountOpenFds(proc_fd_.get()));
183 void NaClSandbox::InitializeLayerTwoSandbox(bool uses_nonsfi_mode) {
184 // seccomp-bpf only applies to the current thread, so it's critical to only
185 // have a single thread running here.
186 DCHECK(!layer_one_sealed_);
187 CHECK(IsSingleThreaded());
188 CheckForExpectedNumberOfOpenFds();
190 RestrictAddressSpaceUsage();
192 base::ScopedFD proc_self_task(GetProcSelfTask(proc_fd_.get()));
194 if (uses_nonsfi_mode) {
195 layer_two_enabled_ =
196 nacl::nonsfi::InitializeBPFSandbox(proc_self_task.Pass());
197 layer_two_is_nonsfi_ = true;
198 } else {
199 layer_two_enabled_ = nacl::InitializeBPFSandbox(proc_self_task.Pass());
203 void NaClSandbox::SealLayerOneSandbox() {
204 if (!layer_two_enabled_) {
205 // If nothing prevents us, check that there is no superfluous directory
206 // open.
207 CHECK(!HasOpenDirectory());
209 proc_fd_.reset();
210 layer_one_sealed_ = true;
213 void NaClSandbox::CheckSandboxingStateWithPolicy() {
214 static const char kItIsDangerousMsg[] = " this is dangerous.";
215 static const char kItIsNotAllowedMsg[] =
216 " this is not allowed in this configuration.";
218 const bool no_sandbox_for_nonsfi_ok =
219 base::CommandLine::ForCurrentProcess()->HasSwitch(
220 switches::kNaClDangerousNoSandboxNonSfi);
221 const bool can_be_no_sandbox =
222 !layer_two_is_nonsfi_ || no_sandbox_for_nonsfi_ok;
224 if (!layer_one_enabled_ || !layer_one_sealed_) {
225 static const char kNoSuidMsg[] =
226 "The SUID sandbox is not engaged for NaCl:";
227 if (can_be_no_sandbox)
228 LOG(ERROR) << kNoSuidMsg << kItIsDangerousMsg;
229 else
230 LOG(FATAL) << kNoSuidMsg << kItIsNotAllowedMsg;
233 if (!layer_two_enabled_) {
234 static const char kNoBpfMsg[] =
235 "The seccomp-bpf sandbox is not engaged for NaCl:";
236 if (can_be_no_sandbox)
237 LOG(ERROR) << kNoBpfMsg << kItIsDangerousMsg;
238 else
239 LOG(FATAL) << kNoBpfMsg << kItIsNotAllowedMsg;
243 } // namespace nacl