1 // Copyright (c) 2012 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.
4
5 #include <dirent.h>
6 #include <fcntl.h>
7 #include <sys/resource.h>
8 #include <sys/stat.h>
9 #include <sys/time.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12
13 #include <limits>
14
15 #include "base/bind.h"
16 #include "base/callback_helpers.h"
17 #include "base/command_line.h"
18 #include "base/logging.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/memory/singleton.h"
21 #include "base/posix/eintr_wrapper.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/time/time.h"
24 #include "build/build_config.h"
25 #include "content/common/sandbox_linux/sandbox_linux.h"
26 #include "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h"
27 #include "content/public/common/content_switches.h"
28 #include "content/public/common/sandbox_linux.h"
29 #include "sandbox/linux/services/credentials.h"
30 #include "sandbox/linux/services/thread_helpers.h"
31 #include "sandbox/linux/suid/client/setuid_sandbox_client.h"
32
33 namespace {
34
35 struct FDCloser {
operator ()__anon5477d2960111::FDCloser36 inline void operator()(int* fd) const {
37 DCHECK(fd);
38 PCHECK(0 == IGNORE_EINTR(close(*fd)));
39 *fd = -1;
40 }
41 };
42
43 // Don't use base::ScopedFD since it doesn't CHECK that the file descriptor was
44 // closed.
45 typedef scoped_ptr<int, FDCloser> SafeScopedFD;
46
LogSandboxStarted(const std::string & sandbox_name)47 void LogSandboxStarted(const std::string& sandbox_name) {
48 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
49 const std::string process_type =
50 command_line.GetSwitchValueASCII(switches::kProcessType);
51 const std::string activated_sandbox =
52 "Activated " + sandbox_name + " sandbox for process type: " +
53 process_type + ".";
54 #if defined(OS_CHROMEOS)
55 LOG(WARNING) << activated_sandbox;
56 #else
57 VLOG(1) << activated_sandbox;
58 #endif
59 }
60
AddResourceLimit(int resource,rlim_t limit)61 bool AddResourceLimit(int resource, rlim_t limit) {
62 struct rlimit old_rlimit;
63 if (getrlimit(resource, &old_rlimit))
64 return false;
65 // Make sure we don't raise the existing limit.
66 const struct rlimit new_rlimit = {
67 std::min(old_rlimit.rlim_cur, limit),
68 std::min(old_rlimit.rlim_max, limit)
69 };
70 int rc = setrlimit(resource, &new_rlimit);
71 return rc == 0;
72 }
73
IsRunningTSAN()74 bool IsRunningTSAN() {
75 #if defined(THREAD_SANITIZER)
76 return true;
77 #else
78 return false;
79 #endif
80 }
81
82 // Try to open /proc/self/task/ with the help of |proc_fd|. |proc_fd| can be
83 // -1. Will return -1 on error and set errno like open(2).
OpenProcTaskFd(int proc_fd)84 int OpenProcTaskFd(int proc_fd) {
85 int proc_self_task = -1;
86 if (proc_fd >= 0) {
87 // If a handle to /proc is available, use it. This allows to bypass file
88 // system restrictions.
89 proc_self_task = openat(proc_fd, "self/task/", O_RDONLY | O_DIRECTORY);
90 } else {
91 // Otherwise, make an attempt to access the file system directly.
92 proc_self_task = open("/proc/self/task/", O_RDONLY | O_DIRECTORY);
93 }
94 return proc_self_task;
95 }
96
97 } // namespace
98
99 namespace content {
100
LinuxSandbox()101 LinuxSandbox::LinuxSandbox()
102 : proc_fd_(-1),
103 seccomp_bpf_started_(false),
104 sandbox_status_flags_(kSandboxLinuxInvalid),
105 pre_initialized_(false),
106 seccomp_bpf_supported_(false),
107 setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) {
108 if (setuid_sandbox_client_ == NULL) {
109 LOG(FATAL) << "Failed to instantiate the setuid sandbox client.";
110 }
111 }
112
~LinuxSandbox()113 LinuxSandbox::~LinuxSandbox() {
114 }
115
GetInstance()116 LinuxSandbox* LinuxSandbox::GetInstance() {
117 LinuxSandbox* instance = Singleton<LinuxSandbox>::get();
118 CHECK(instance);
119 return instance;
120 }
121
122 #if defined(ADDRESS_SANITIZER) && defined(OS_LINUX)
123 // ASan API call to notify the tool the sandbox is going to be turned on.
124 extern "C" void __sanitizer_sandbox_on_notify(void *reserved);
125 #endif
126
PreinitializeSandbox()127 void LinuxSandbox::PreinitializeSandbox() {
128 CHECK(!pre_initialized_);
129 seccomp_bpf_supported_ = false;
130 #if defined(ADDRESS_SANITIZER) && defined(OS_LINUX)
131 // ASan needs to open some resources before the sandbox is enabled.
132 // This should not fork, not launch threads, not open a directory.
133 __sanitizer_sandbox_on_notify(/*reserved*/NULL);
134 #endif
135
136 #if !defined(NDEBUG)
137 // Open proc_fd_ only in Debug mode so that forgetting to close it doesn't
138 // produce a sandbox escape in Release mode.
139 proc_fd_ = open("/proc", O_DIRECTORY | O_RDONLY);
140 CHECK_GE(proc_fd_, 0);
141 #endif // !defined(NDEBUG)
142 // We "pre-warm" the code that detects supports for seccomp BPF.
143 if (SandboxSeccompBPF::IsSeccompBPFDesired()) {
144 if (!SandboxSeccompBPF::SupportsSandbox()) {
145 VLOG(1) << "Lacking support for seccomp-bpf sandbox.";
146 } else {
147 seccomp_bpf_supported_ = true;
148 }
149 }
150 pre_initialized_ = true;
151 }
152
InitializeSandbox()153 bool LinuxSandbox::InitializeSandbox() {
154 LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance();
155 return linux_sandbox->InitializeSandboxImpl();
156 }
157
StopThread(base::Thread * thread)158 void LinuxSandbox::StopThread(base::Thread* thread) {
159 LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance();
160 linux_sandbox->StopThreadImpl(thread);
161 }
162
GetStatus()163 int LinuxSandbox::GetStatus() {
164 CHECK(pre_initialized_);
165 if (kSandboxLinuxInvalid == sandbox_status_flags_) {
166 // Initialize sandbox_status_flags_.
167 sandbox_status_flags_ = 0;
168 if (setuid_sandbox_client_->IsSandboxed()) {
169 sandbox_status_flags_ |= kSandboxLinuxSUID;
170 if (setuid_sandbox_client_->IsInNewPIDNamespace())
171 sandbox_status_flags_ |= kSandboxLinuxPIDNS;
172 if (setuid_sandbox_client_->IsInNewNETNamespace())
173 sandbox_status_flags_ |= kSandboxLinuxNetNS;
174 }
175
176 // We report whether the sandbox will be activated when renderers, workers
177 // and PPAPI plugins go through sandbox initialization.
178 if (seccomp_bpf_supported() &&
179 SandboxSeccompBPF::ShouldEnableSeccompBPF(switches::kRendererProcess)) {
180 sandbox_status_flags_ |= kSandboxLinuxSeccompBPF;
181 }
182 }
183
184 return sandbox_status_flags_;
185 }
186
187 // Threads are counted via /proc/self/task. This is a little hairy because of
188 // PID namespaces and existing sandboxes, so "self" must really be used instead
189 // of using the pid.
IsSingleThreaded() const190 bool LinuxSandbox::IsSingleThreaded() const {
191 bool is_single_threaded = false;
192 int proc_self_task = OpenProcTaskFd(proc_fd_);
193
194 // In Debug mode, it's mandatory to be able to count threads to catch bugs.
195 #if !defined(NDEBUG)
196 // Using CHECK here since we want to check all the cases where
197 // !defined(NDEBUG)
198 // gets built.
199 CHECK_LE(0, proc_self_task) << "Could not count threads, the sandbox was not "
200 << "pre-initialized properly.";
201 #endif // !defined(NDEBUG)
202
203 if (proc_self_task < 0) {
204 // Pretend to be monothreaded if it can't be determined (for instance the
205 // setuid sandbox is already engaged but no proc_fd_ is available).
206 is_single_threaded = true;
207 } else {
208 SafeScopedFD task_closer(&proc_self_task);
209 is_single_threaded =
210 sandbox::ThreadHelpers::IsSingleThreaded(proc_self_task);
211 }
212
213 return is_single_threaded;
214 }
215
seccomp_bpf_started() const216 bool LinuxSandbox::seccomp_bpf_started() const {
217 return seccomp_bpf_started_;
218 }
219
220 sandbox::SetuidSandboxClient*
setuid_sandbox_client() const221 LinuxSandbox::setuid_sandbox_client() const {
222 return setuid_sandbox_client_.get();
223 }
224
225 // For seccomp-bpf, we use the SandboxSeccompBPF class.
StartSeccompBPF(const std::string & process_type)226 bool LinuxSandbox::StartSeccompBPF(const std::string& process_type) {
227 CHECK(!seccomp_bpf_started_);
228 CHECK(pre_initialized_);
229 if (seccomp_bpf_supported())
230 seccomp_bpf_started_ = SandboxSeccompBPF::StartSandbox(process_type);
231
232 if (seccomp_bpf_started_)
233 LogSandboxStarted("seccomp-bpf");
234
235 return seccomp_bpf_started_;
236 }
237
InitializeSandboxImpl()238 bool LinuxSandbox::InitializeSandboxImpl() {
239 const std::string process_type =
240 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
241 switches::kProcessType);
242
243 // We need to make absolutely sure that our sandbox is "sealed" before
244 // returning.
245 // Unretained() since the current object is a Singleton.
246 base::ScopedClosureRunner sandbox_sealer(
247 base::Bind(&LinuxSandbox::SealSandbox, base::Unretained(this)));
248 // Make sure that this function enables sandboxes as promised by GetStatus().
249 // Unretained() since the current object is a Singleton.
250 base::ScopedClosureRunner sandbox_promise_keeper(
251 base::Bind(&LinuxSandbox::CheckForBrokenPromises,
252 base::Unretained(this),
253 process_type));
254
255 // No matter what, it's always an error to call InitializeSandbox() after
256 // threads have been created.
257 if (!IsSingleThreaded()) {
258 std::string error_message = "InitializeSandbox() called with multiple "
259 "threads in process " + process_type;
260 // TSAN starts a helper thread. So we don't start the sandbox and don't
261 // even report an error about it.
262 if (IsRunningTSAN())
263 return false;
264 // The GPU process is allowed to call InitializeSandbox() with threads for
265 // now, because it loads third-party libraries.
266 if (process_type != switches::kGpuProcess)
267 CHECK(false) << error_message;
268 LOG(ERROR) << error_message;
269 return false;
270 }
271
272 // Only one thread is running, pre-initialize if not already done.
273 if (!pre_initialized_)
274 PreinitializeSandbox();
275
276 DCHECK(!HasOpenDirectories()) <<
277 "InitializeSandbox() called after unexpected directories have been " <<
278 "opened. This breaks the security of the setuid sandbox.";
279
280 // Attempt to limit the future size of the address space of the process.
281 LimitAddressSpace(process_type);
282
283 // Try to enable seccomp-bpf.
284 bool seccomp_bpf_started = StartSeccompBPF(process_type);
285
286 return seccomp_bpf_started;
287 }
288
StopThreadImpl(base::Thread * thread)289 void LinuxSandbox::StopThreadImpl(base::Thread* thread) {
290 DCHECK(thread);
291 StopThreadAndEnsureNotCounted(thread);
292 }
293
seccomp_bpf_supported() const294 bool LinuxSandbox::seccomp_bpf_supported() const {
295 CHECK(pre_initialized_);
296 return seccomp_bpf_supported_;
297 }
298
LimitAddressSpace(const std::string & process_type)299 bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) {
300 (void) process_type;
301 #if !defined(ADDRESS_SANITIZER)
302 CommandLine* command_line = CommandLine::ForCurrentProcess();
303 if (command_line->HasSwitch(switches::kNoSandbox)) {
304 return false;
305 }
306
307 // Limit the address space to 4GB.
308 // This is in the hope of making some kernel exploits more complex and less
309 // reliable. It also limits sprays a little on 64-bit.
310 rlim_t address_space_limit = std::numeric_limits<uint32_t>::max();
311 #if defined(__LP64__)
312 // On 64 bits, V8 and possibly others will reserve massive memory ranges and
313 // rely on on-demand paging for allocation. Unfortunately, even
314 // MADV_DONTNEED ranges count towards RLIMIT_AS so this is not an option.
315 // See crbug.com/169327 for a discussion.
316 // On the GPU process, irrespective of V8, we can exhaust a 4GB address space
317 // under normal usage, see crbug.com/271119
318 // For now, increase limit to 16GB for renderer and worker and gpu processes
319 // to accomodate.
320 if (process_type == switches::kRendererProcess ||
321 process_type == switches::kWorkerProcess ||
322 process_type == switches::kGpuProcess) {
323 address_space_limit = 1L << 34;
324 }
325 #endif // defined(__LP64__)
326
327 // On all platforms, add a limit to the brk() heap that would prevent
328 // allocations that can't be index by an int.
329 const rlim_t kNewDataSegmentMaxSize = std::numeric_limits<int>::max();
330
331 bool limited_as = AddResourceLimit(RLIMIT_AS, address_space_limit);
332 bool limited_data = AddResourceLimit(RLIMIT_DATA, kNewDataSegmentMaxSize);
333 return limited_as && limited_data;
334 #else
335 return false;
336 #endif // !defined(ADDRESS_SANITIZER)
337 }
338
HasOpenDirectories() const339 bool LinuxSandbox::HasOpenDirectories() const {
340 return sandbox::Credentials().HasOpenDirectory(proc_fd_);
341 }
342
SealSandbox()343 void LinuxSandbox::SealSandbox() {
344 if (proc_fd_ >= 0) {
345 int ret = IGNORE_EINTR(close(proc_fd_));
346 CHECK_EQ(0, ret);
347 proc_fd_ = -1;
348 }
349 }
350
CheckForBrokenPromises(const std::string & process_type)351 void LinuxSandbox::CheckForBrokenPromises(const std::string& process_type) {
352 // Make sure that any promise made with GetStatus() wasn't broken.
353 bool promised_seccomp_bpf_would_start = false;
354 if (process_type == switches::kRendererProcess ||
355 process_type == switches::kWorkerProcess ||
356 process_type == switches::kPpapiPluginProcess) {
357 promised_seccomp_bpf_would_start =
358 (sandbox_status_flags_ != kSandboxLinuxInvalid) &&
359 (GetStatus() & kSandboxLinuxSeccompBPF);
360 }
361 if (promised_seccomp_bpf_would_start) {
362 CHECK(seccomp_bpf_started_);
363 }
364 }
365
StopThreadAndEnsureNotCounted(base::Thread * thread) const366 void LinuxSandbox::StopThreadAndEnsureNotCounted(base::Thread* thread) const {
367 DCHECK(thread);
368 int proc_self_task = OpenProcTaskFd(proc_fd_);
369 PCHECK(proc_self_task >= 0);
370 SafeScopedFD task_closer(&proc_self_task);
371 CHECK(
372 sandbox::ThreadHelpers::StopThreadAndWatchProcFS(proc_self_task, thread));
373 }
374
375 } // namespace content
376