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
33namespace {
34
35struct FDCloser {
36  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.
45typedef scoped_ptr<int, FDCloser> SafeScopedFD;
46
47void 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
61bool 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
74bool 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).
84int 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
99namespace content {
100
101LinuxSandbox::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
113LinuxSandbox::~LinuxSandbox() {
114}
115
116LinuxSandbox* 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.
124extern "C" void __sanitizer_sandbox_on_notify(void *reserved);
125#endif
126
127void 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
153bool LinuxSandbox::InitializeSandbox() {
154  LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance();
155  return linux_sandbox->InitializeSandboxImpl();
156}
157
158void LinuxSandbox::StopThread(base::Thread* thread) {
159  LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance();
160  linux_sandbox->StopThreadImpl(thread);
161}
162
163int 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.
190bool 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
216bool LinuxSandbox::seccomp_bpf_started() const {
217  return seccomp_bpf_started_;
218}
219
220sandbox::SetuidSandboxClient*
221    LinuxSandbox::setuid_sandbox_client() const {
222  return setuid_sandbox_client_.get();
223}
224
225// For seccomp-bpf, we use the SandboxSeccompBPF class.
226bool 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
238bool 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
289void LinuxSandbox::StopThreadImpl(base::Thread* thread) {
290  DCHECK(thread);
291  StopThreadAndEnsureNotCounted(thread);
292}
293
294bool LinuxSandbox::seccomp_bpf_supported() const {
295  CHECK(pre_initialized_);
296  return seccomp_bpf_supported_;
297}
298
299bool 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
339bool LinuxSandbox::HasOpenDirectories() const {
340  return sandbox::Credentials().HasOpenDirectory(proc_fd_);
341}
342
343void 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
351void 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
366void 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