• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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