Home | History | Annotate | Download | only in sandbox_linux
      1 // Copyright (c) 2013 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 "content/common/sandbox_linux/bpf_gpu_policy_linux.h"
      6 
      7 #include <dlfcn.h>
      8 #include <errno.h>
      9 #include <fcntl.h>
     10 #include <sys/socket.h>
     11 #include <sys/stat.h>
     12 #include <sys/types.h>
     13 #include <unistd.h>
     14 
     15 #include <string>
     16 #include <vector>
     17 
     18 #include "base/bind.h"
     19 #include "base/command_line.h"
     20 #include "base/compiler_specific.h"
     21 #include "base/logging.h"
     22 #include "base/memory/scoped_ptr.h"
     23 #include "build/build_config.h"
     24 #include "content/common/sandbox_linux/sandbox_bpf_base_policy_linux.h"
     25 #include "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h"
     26 #include "content/common/set_process_title.h"
     27 #include "content/public/common/content_switches.h"
     28 #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
     29 #include "sandbox/linux/seccomp-bpf/trap.h"
     30 #include "sandbox/linux/services/broker_process.h"
     31 #include "sandbox/linux/services/linux_syscalls.h"
     32 
     33 using sandbox::BrokerProcess;
     34 using sandbox::SyscallSets;
     35 using sandbox::arch_seccomp_data;
     36 using sandbox::bpf_dsl::Allow;
     37 using sandbox::bpf_dsl::ResultExpr;
     38 
     39 namespace content {
     40 
     41 namespace {
     42 
     43 inline bool IsChromeOS() {
     44 #if defined(OS_CHROMEOS)
     45   return true;
     46 #else
     47   return false;
     48 #endif
     49 }
     50 
     51 inline bool IsArchitectureX86_64() {
     52 #if defined(__x86_64__)
     53   return true;
     54 #else
     55   return false;
     56 #endif
     57 }
     58 
     59 inline bool IsArchitectureI386() {
     60 #if defined(__i386__)
     61   return true;
     62 #else
     63   return false;
     64 #endif
     65 }
     66 
     67 inline bool IsArchitectureArm() {
     68 #if defined(__arm__)
     69   return true;
     70 #else
     71   return false;
     72 #endif
     73 }
     74 
     75 bool IsAcceleratedVideoEnabled() {
     76   const base::CommandLine& command_line =
     77       *base::CommandLine::ForCurrentProcess();
     78   bool accelerated_encode_enabled = false;
     79 #if defined(OS_CHROMEOS)
     80   accelerated_encode_enabled =
     81       !command_line.HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode);
     82 #endif
     83   return !command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode) ||
     84          accelerated_encode_enabled;
     85 }
     86 
     87 intptr_t GpuSIGSYS_Handler(const struct arch_seccomp_data& args,
     88                            void* aux_broker_process) {
     89   RAW_CHECK(aux_broker_process);
     90   BrokerProcess* broker_process =
     91       static_cast<BrokerProcess*>(aux_broker_process);
     92   switch (args.nr) {
     93     case __NR_access:
     94       return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
     95                                     static_cast<int>(args.args[1]));
     96     case __NR_open:
     97 #if defined(MEMORY_SANITIZER)
     98       // http://crbug.com/372840
     99       __msan_unpoison_string(reinterpret_cast<const char*>(args.args[0]));
    100 #endif
    101       return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
    102                                   static_cast<int>(args.args[1]));
    103     case __NR_openat:
    104       // Allow using openat() as open().
    105       if (static_cast<int>(args.args[0]) == AT_FDCWD) {
    106         return
    107             broker_process->Open(reinterpret_cast<const char*>(args.args[1]),
    108                                  static_cast<int>(args.args[2]));
    109       } else {
    110         return -EPERM;
    111       }
    112     default:
    113       RAW_CHECK(false);
    114       return -ENOSYS;
    115   }
    116 }
    117 
    118 class GpuBrokerProcessPolicy : public GpuProcessPolicy {
    119  public:
    120   static sandbox::bpf_dsl::SandboxBPFDSLPolicy* Create() {
    121     return new GpuBrokerProcessPolicy();
    122   }
    123   virtual ~GpuBrokerProcessPolicy() {}
    124 
    125   virtual ResultExpr EvaluateSyscall(int system_call_number) const OVERRIDE;
    126 
    127  private:
    128   GpuBrokerProcessPolicy() {}
    129   DISALLOW_COPY_AND_ASSIGN(GpuBrokerProcessPolicy);
    130 };
    131 
    132 // x86_64/i386 or desktop ARM.
    133 // A GPU broker policy is the same as a GPU policy with open and
    134 // openat allowed.
    135 ResultExpr GpuBrokerProcessPolicy::EvaluateSyscall(int sysno) const {
    136   switch (sysno) {
    137     case __NR_access:
    138     case __NR_open:
    139     case __NR_openat:
    140       return Allow();
    141     default:
    142       return GpuProcessPolicy::EvaluateSyscall(sysno);
    143   }
    144 }
    145 
    146 void UpdateProcessTypeToGpuBroker() {
    147   base::CommandLine::StringVector exec =
    148       base::CommandLine::ForCurrentProcess()->GetArgs();
    149   base::CommandLine::Reset();
    150   base::CommandLine::Init(0, NULL);
    151   base::CommandLine::ForCurrentProcess()->InitFromArgv(exec);
    152   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
    153       switches::kProcessType, "gpu-broker");
    154 
    155   // Update the process title. The argv was already cached by the call to
    156   // SetProcessTitleFromCommandLine in content_main_runner.cc, so we can pass
    157   // NULL here (we don't have the original argv at this point).
    158   SetProcessTitleFromCommandLine(NULL);
    159 }
    160 
    161 bool UpdateProcessTypeAndEnableSandbox(sandbox::bpf_dsl::SandboxBPFDSLPolicy* (
    162     *broker_sandboxer_allocator)(void)) {
    163   DCHECK(broker_sandboxer_allocator);
    164   UpdateProcessTypeToGpuBroker();
    165   return SandboxSeccompBPF::StartSandboxWithExternalPolicy(
    166       make_scoped_ptr(broker_sandboxer_allocator()));
    167 }
    168 
    169 }  // namespace
    170 
    171 GpuProcessPolicy::GpuProcessPolicy() : broker_process_(NULL) {}
    172 
    173 GpuProcessPolicy::~GpuProcessPolicy() {}
    174 
    175 // Main policy for x86_64/i386. Extended by CrosArmGpuProcessPolicy.
    176 ResultExpr GpuProcessPolicy::EvaluateSyscall(int sysno) const {
    177   switch (sysno) {
    178     case __NR_ioctl:
    179 #if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
    180     // The Nvidia driver uses flags not in the baseline policy
    181     // (MAP_LOCKED | MAP_EXECUTABLE | MAP_32BIT)
    182     case __NR_mmap:
    183 #endif
    184     // We also hit this on the linux_chromeos bot but don't yet know what
    185     // weird flags were involved.
    186     case __NR_mprotect:
    187     // TODO(jln): restrict prctl.
    188     case __NR_prctl:
    189     case __NR_sched_getaffinity:
    190     case __NR_sched_setaffinity:
    191     case __NR_setpriority:
    192       return Allow();
    193     case __NR_access:
    194     case __NR_open:
    195     case __NR_openat:
    196       DCHECK(broker_process_);
    197       return Trap(GpuSIGSYS_Handler, broker_process_);
    198     default:
    199       if (SyscallSets::IsEventFd(sysno))
    200         return Allow();
    201 
    202       // Default on the baseline policy.
    203       return SandboxBPFBasePolicy::EvaluateSyscall(sysno);
    204   }
    205 }
    206 
    207 bool GpuProcessPolicy::PreSandboxHook() {
    208   // Warm up resources needed by the policy we're about to enable and
    209   // eventually start a broker process.
    210   const bool chromeos_arm_gpu = IsChromeOS() && IsArchitectureArm();
    211   // This policy is for x86 or Desktop.
    212   DCHECK(!chromeos_arm_gpu);
    213 
    214   DCHECK(!broker_process());
    215   // Create a new broker process.
    216   InitGpuBrokerProcess(
    217       GpuBrokerProcessPolicy::Create,
    218       std::vector<std::string>(),  // No extra files in whitelist.
    219       std::vector<std::string>());
    220 
    221   if (IsArchitectureX86_64() || IsArchitectureI386()) {
    222     // Accelerated video dlopen()'s some shared objects
    223     // inside the sandbox, so preload them now.
    224     if (IsAcceleratedVideoEnabled()) {
    225       const char* I965DrvVideoPath = NULL;
    226 
    227       if (IsArchitectureX86_64()) {
    228         I965DrvVideoPath = "/usr/lib64/va/drivers/i965_drv_video.so";
    229       } else if (IsArchitectureI386()) {
    230         I965DrvVideoPath = "/usr/lib/va/drivers/i965_drv_video.so";
    231       }
    232 
    233       dlopen(I965DrvVideoPath, RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
    234       dlopen("libva.so.1", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
    235       dlopen("libva-x11.so.1", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
    236     }
    237   }
    238 
    239   return true;
    240 }
    241 
    242 void GpuProcessPolicy::InitGpuBrokerProcess(
    243     sandbox::bpf_dsl::SandboxBPFDSLPolicy* (*broker_sandboxer_allocator)(void),
    244     const std::vector<std::string>& read_whitelist_extra,
    245     const std::vector<std::string>& write_whitelist_extra) {
    246   static const char kDriRcPath[] = "/etc/drirc";
    247   static const char kDriCard0Path[] = "/dev/dri/card0";
    248 
    249   CHECK(broker_process_ == NULL);
    250 
    251   // All GPU process policies need these files brokered out.
    252   std::vector<std::string> read_whitelist;
    253   read_whitelist.push_back(kDriCard0Path);
    254   read_whitelist.push_back(kDriRcPath);
    255   // Add eventual extra files from read_whitelist_extra.
    256   read_whitelist.insert(read_whitelist.end(),
    257                         read_whitelist_extra.begin(),
    258                         read_whitelist_extra.end());
    259 
    260   std::vector<std::string> write_whitelist;
    261   write_whitelist.push_back(kDriCard0Path);
    262   // Add eventual extra files from write_whitelist_extra.
    263   write_whitelist.insert(write_whitelist.end(),
    264                          write_whitelist_extra.begin(),
    265                          write_whitelist_extra.end());
    266 
    267   broker_process_ = new BrokerProcess(GetFSDeniedErrno(),
    268                                       read_whitelist,
    269                                       write_whitelist);
    270   // The initialization callback will perform generic initialization and then
    271   // call broker_sandboxer_callback.
    272   CHECK(broker_process_->Init(base::Bind(&UpdateProcessTypeAndEnableSandbox,
    273                                          broker_sandboxer_allocator)));
    274 }
    275 
    276 }  // namespace content
    277