Home | History | Annotate | Download | only in sandbox_linux
      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 {
     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.
     45 typedef scoped_ptr<int, FDCloser> SafeScopedFD;
     46 
     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 
     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 
     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).
     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 
    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 
    113 LinuxSandbox::~LinuxSandbox() {
    114 }
    115 
    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 
    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 
    153 bool LinuxSandbox::InitializeSandbox() {
    154   LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance();
    155   return linux_sandbox->InitializeSandboxImpl();
    156 }
    157 
    158 void LinuxSandbox::StopThread(base::Thread* thread) {
    159   LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance();
    160   linux_sandbox->StopThreadImpl(thread);
    161 }
    162 
    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.
    190 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 
    216 bool LinuxSandbox::seccomp_bpf_started() const {
    217   return seccomp_bpf_started_;
    218 }
    219 
    220 sandbox::SetuidSandboxClient*
    221     LinuxSandbox::setuid_sandbox_client() const {
    222   return setuid_sandbox_client_.get();
    223 }
    224 
    225 // For seccomp-bpf, we use the SandboxSeccompBPF class.
    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 
    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 
    289 void LinuxSandbox::StopThreadImpl(base::Thread* thread) {
    290   DCHECK(thread);
    291   StopThreadAndEnsureNotCounted(thread);
    292 }
    293 
    294 bool LinuxSandbox::seccomp_bpf_supported() const {
    295   CHECK(pre_initialized_);
    296   return seccomp_bpf_supported_;
    297 }
    298 
    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 
    339 bool LinuxSandbox::HasOpenDirectories() const {
    340   return sandbox::Credentials().HasOpenDirectory(proc_fd_);
    341 }
    342 
    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 
    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 
    366 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