Home | History | Annotate | Download | only in common
      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 <fcntl.h>
      6 #include <sys/resource.h>
      7 #include <sys/stat.h>
      8 #include <sys/time.h>
      9 #include <sys/types.h>
     10 
     11 #include <limits>
     12 
     13 #include "base/bind.h"
     14 #include "base/bind_helpers.h"
     15 #include "base/command_line.h"
     16 #include "base/logging.h"
     17 #include "base/memory/singleton.h"
     18 #include "base/posix/eintr_wrapper.h"
     19 #include "base/time/time.h"
     20 #include "content/common/sandbox_linux.h"
     21 #include "content/common/sandbox_seccomp_bpf_linux.h"
     22 #include "content/public/common/content_switches.h"
     23 #include "content/public/common/sandbox_linux.h"
     24 #include "sandbox/linux/suid/client/setuid_sandbox_client.h"
     25 
     26 namespace {
     27 
     28 void LogSandboxStarted(const std::string& sandbox_name) {
     29   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
     30   const std::string process_type =
     31       command_line.GetSwitchValueASCII(switches::kProcessType);
     32   const std::string activated_sandbox =
     33       "Activated " + sandbox_name + " sandbox for process type: " +
     34       process_type + ".";
     35 #if defined(OS_CHROMEOS)
     36   LOG(WARNING) << activated_sandbox;
     37 #else
     38   VLOG(1) << activated_sandbox;
     39 #endif
     40 }
     41 
     42 bool AddResourceLimit(int resource, rlim_t limit) {
     43   struct rlimit old_rlimit;
     44   if (getrlimit(resource, &old_rlimit))
     45     return false;
     46   // Make sure we don't raise the existing limit.
     47   const struct rlimit new_rlimit = {
     48       std::min(old_rlimit.rlim_cur, limit),
     49       std::min(old_rlimit.rlim_max, limit)
     50       };
     51   int rc = setrlimit(resource, &new_rlimit);
     52   return rc == 0;
     53 }
     54 
     55 bool IsRunningTSAN() {
     56 #if defined(THREAD_SANITIZER)
     57   return true;
     58 #else
     59   return false;
     60 #endif
     61 }
     62 
     63 }  // namespace
     64 
     65 namespace content {
     66 
     67 LinuxSandbox::LinuxSandbox()
     68     : proc_fd_(-1),
     69       seccomp_bpf_started_(false),
     70       pre_initialized_(false),
     71       seccomp_bpf_supported_(false),
     72       setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) {
     73   if (setuid_sandbox_client_ == NULL) {
     74     LOG(FATAL) << "Failed to instantiate the setuid sandbox client.";
     75   }
     76 }
     77 
     78 LinuxSandbox::~LinuxSandbox() {
     79 }
     80 
     81 LinuxSandbox* LinuxSandbox::GetInstance() {
     82   LinuxSandbox* instance = Singleton<LinuxSandbox>::get();
     83   CHECK(instance);
     84   return instance;
     85 }
     86 
     87 #if defined(ADDRESS_SANITIZER) && defined(OS_LINUX)
     88 // ASan API call to notify the tool the sandbox is going to be turned on.
     89 extern "C" void __sanitizer_sandbox_on_notify(void *reserved);
     90 #endif
     91 
     92 void LinuxSandbox::PreinitializeSandbox() {
     93   CHECK(!pre_initialized_);
     94   seccomp_bpf_supported_ = false;
     95 #if defined(ADDRESS_SANITIZER) && defined(OS_LINUX)
     96   // ASan needs to open some resources before the sandbox is enabled.
     97   // This should not fork, not launch threads, not open a directory.
     98   __sanitizer_sandbox_on_notify(/*reserved*/NULL);
     99 #endif
    100 
    101 #if !defined(NDEBUG)
    102   // Open proc_fd_ only in Debug mode so that forgetting to close it doesn't
    103   // produce a sandbox escape in Release mode.
    104   proc_fd_ = open("/proc", O_DIRECTORY | O_RDONLY);
    105   CHECK_GE(proc_fd_, 0);
    106 #endif  // !defined(NDEBUG)
    107   // We "pre-warm" the code that detects supports for seccomp BPF.
    108   if (SandboxSeccompBpf::IsSeccompBpfDesired()) {
    109     if (!SandboxSeccompBpf::SupportsSandbox()) {
    110       VLOG(1) << "Lacking support for seccomp-bpf sandbox.";
    111     } else {
    112       seccomp_bpf_supported_ = true;
    113     }
    114   }
    115   pre_initialized_ = true;
    116 }
    117 
    118 bool LinuxSandbox::InitializeSandbox() {
    119   bool seccomp_bpf_started = false;
    120   LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance();
    121   // We need to make absolutely sure that our sandbox is "sealed" before
    122   // InitializeSandbox does exit.
    123   base::ScopedClosureRunner sandbox_sealer(
    124       base::Bind(&LinuxSandbox::SealSandbox, base::Unretained(linux_sandbox)));
    125   const std::string process_type =
    126       CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
    127           switches::kProcessType);
    128 
    129   // No matter what, it's always an error to call InitializeSandbox() after
    130   // threads have been created.
    131   if (!linux_sandbox->IsSingleThreaded()) {
    132     std::string error_message = "InitializeSandbox() called with multiple "
    133                                 "threads in process " + process_type;
    134     // TSAN starts a helper thread. So we don't start the sandbox and don't
    135     // even report an error about it.
    136     if (IsRunningTSAN())
    137       return false;
    138     // The GPU process is allowed to call InitializeSandbox() with threads for
    139     // now, because it loads third party libraries.
    140     if (process_type != switches::kGpuProcess)
    141       CHECK(false) << error_message;
    142     LOG(ERROR) << error_message;
    143     return false;
    144   }
    145 
    146   // Attempt to limit the future size of the address space of the process.
    147   linux_sandbox->LimitAddressSpace(process_type);
    148 
    149   // First, try to enable seccomp-bpf.
    150   seccomp_bpf_started = linux_sandbox->StartSeccompBpf(process_type);
    151 
    152   return seccomp_bpf_started;
    153 }
    154 
    155 int LinuxSandbox::GetStatus() const {
    156   CHECK(pre_initialized_);
    157   int sandbox_flags = 0;
    158   if (setuid_sandbox_client_->IsSandboxed()) {
    159     sandbox_flags |= kSandboxLinuxSUID;
    160     if (setuid_sandbox_client_->IsInNewPIDNamespace())
    161       sandbox_flags |= kSandboxLinuxPIDNS;
    162     if (setuid_sandbox_client_->IsInNewNETNamespace())
    163       sandbox_flags |= kSandboxLinuxNetNS;
    164   }
    165 
    166   if (seccomp_bpf_supported() &&
    167       SandboxSeccompBpf::ShouldEnableSeccompBpf(switches::kRendererProcess)) {
    168     // We report whether the sandbox will be activated when renderers go
    169     // through sandbox initialization.
    170     sandbox_flags |= kSandboxLinuxSeccompBpf;
    171   }
    172 
    173   return sandbox_flags;
    174 }
    175 
    176 // Threads are counted via /proc/self/task. This is a little hairy because of
    177 // PID namespaces and existing sandboxes, so "self" must really be used instead
    178 // of using the pid.
    179 bool LinuxSandbox::IsSingleThreaded() const {
    180   struct stat task_stat;
    181   int fstat_ret;
    182   if (proc_fd_ >= 0) {
    183     // If a handle to /proc is available, use it. This allows to bypass file
    184     // system restrictions.
    185     fstat_ret = fstatat(proc_fd_, "self/task/", &task_stat, 0);
    186   } else {
    187     // Otherwise, make an attempt to access the file system directly.
    188     fstat_ret = fstatat(AT_FDCWD, "/proc/self/task/", &task_stat, 0);
    189   }
    190   // In Debug mode, it's mandatory to be able to count threads to catch bugs.
    191 #if !defined(NDEBUG)
    192   // Using DCHECK here would be incorrect. DCHECK can be enabled in non
    193   // official release mode.
    194   CHECK_EQ(0, fstat_ret) << "Could not count threads, the sandbox was not "
    195                          << "pre-initialized properly.";
    196 #endif  // !defined(NDEBUG)
    197   if (fstat_ret) {
    198     // Pretend to be monothreaded if it can't be determined (for instance the
    199     // setuid sandbox is already engaged but no proc_fd_ is available).
    200     return true;
    201   }
    202 
    203   // At least "..", "." and the current thread should be present.
    204   CHECK_LE(3UL, task_stat.st_nlink);
    205   // Counting threads via /proc/self/task could be racy. For the purpose of
    206   // determining if the current proces is monothreaded it works: if at any
    207   // time it becomes monothreaded, it'll stay so.
    208   return task_stat.st_nlink == 3;
    209 }
    210 
    211 bool LinuxSandbox::seccomp_bpf_started() const {
    212   return seccomp_bpf_started_;
    213 }
    214 
    215 sandbox::SetuidSandboxClient*
    216     LinuxSandbox::setuid_sandbox_client() const {
    217   return setuid_sandbox_client_.get();
    218 }
    219 
    220 // For seccomp-bpf, we use the SandboxSeccompBpf class.
    221 bool LinuxSandbox::StartSeccompBpf(const std::string& process_type) {
    222   CHECK(!seccomp_bpf_started_);
    223   if (!pre_initialized_)
    224     PreinitializeSandbox();
    225   if (seccomp_bpf_supported())
    226     seccomp_bpf_started_ = SandboxSeccompBpf::StartSandbox(process_type);
    227 
    228   if (seccomp_bpf_started_)
    229     LogSandboxStarted("seccomp-bpf");
    230 
    231   return seccomp_bpf_started_;
    232 }
    233 
    234 bool LinuxSandbox::seccomp_bpf_supported() const {
    235   CHECK(pre_initialized_);
    236   return seccomp_bpf_supported_;
    237 }
    238 
    239 bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) {
    240   (void) process_type;
    241 #if !defined(ADDRESS_SANITIZER)
    242   CommandLine* command_line = CommandLine::ForCurrentProcess();
    243   if (command_line->HasSwitch(switches::kNoSandbox)) {
    244     return false;
    245   }
    246 
    247   // Limit the address space to 4GB.
    248   // This is in the hope of making some kernel exploits more complex and less
    249   // reliable. It also limits sprays a little on 64-bit.
    250   rlim_t address_space_limit = std::numeric_limits<uint32_t>::max();
    251 #if defined(__LP64__)
    252   // On 64 bits, V8 and possibly others will reserve massive memory ranges and
    253   // rely on on-demand paging for allocation.  Unfortunately, even
    254   // MADV_DONTNEED ranges  count towards RLIMIT_AS so this is not an option.
    255   // See crbug.com/169327 for a discussion.
    256   // For now, increase limit to 16GB for renderer and worker processes to
    257   // accomodate.
    258   if (process_type == switches::kRendererProcess ||
    259       process_type == switches::kWorkerProcess) {
    260     address_space_limit = 1L << 34;
    261   }
    262 #endif  // defined(__LP64__)
    263 
    264   // On all platforms, add a limit to the brk() heap that would prevent
    265   // allocations that can't be index by an int.
    266   const rlim_t kNewDataSegmentMaxSize = std::numeric_limits<int>::max();
    267 
    268   bool limited_as = AddResourceLimit(RLIMIT_AS, address_space_limit);
    269   bool limited_data = AddResourceLimit(RLIMIT_DATA, kNewDataSegmentMaxSize);
    270   return limited_as && limited_data;
    271 #else
    272   return false;
    273 #endif  // !defined(ADDRESS_SANITIZER)
    274 }
    275 
    276 void LinuxSandbox::SealSandbox() {
    277   if (proc_fd_ >= 0) {
    278     int ret = HANDLE_EINTR(close(proc_fd_));
    279     CHECK_EQ(0, ret);
    280     proc_fd_ = -1;
    281   }
    282 }
    283 
    284 }  // namespace content
    285 
    286