// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/common/sandbox_linux/bpf_gpu_policy_linux.h" #include #include #include #include #include #include #include #include #include #include "base/bind.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "build/build_config.h" #include "content/common/sandbox_linux/sandbox_bpf_base_policy_linux.h" #include "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h" #include "content/common/set_process_title.h" #include "content/public/common/content_switches.h" #include "sandbox/linux/bpf_dsl/bpf_dsl.h" #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" #include "sandbox/linux/services/broker_process.h" #include "sandbox/linux/services/linux_syscalls.h" using sandbox::BrokerProcess; using sandbox::SyscallSets; using sandbox::arch_seccomp_data; using sandbox::bpf_dsl::Allow; using sandbox::bpf_dsl::ResultExpr; using sandbox::bpf_dsl::Trap; namespace content { namespace { inline bool IsChromeOS() { #if defined(OS_CHROMEOS) return true; #else return false; #endif } inline bool IsArchitectureX86_64() { #if defined(__x86_64__) return true; #else return false; #endif } inline bool IsArchitectureI386() { #if defined(__i386__) return true; #else return false; #endif } inline bool IsArchitectureArm() { #if defined(__arm__) return true; #else return false; #endif } bool IsAcceleratedVideoEnabled() { const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); bool accelerated_encode_enabled = false; #if defined(OS_CHROMEOS) accelerated_encode_enabled = !command_line.HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode); #endif return !command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode) || accelerated_encode_enabled; } intptr_t GpuSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux_broker_process) { RAW_CHECK(aux_broker_process); BrokerProcess* broker_process = static_cast(aux_broker_process); switch (args.nr) { case __NR_access: return broker_process->Access(reinterpret_cast(args.args[0]), static_cast(args.args[1])); case __NR_open: #if defined(MEMORY_SANITIZER) // http://crbug.com/372840 __msan_unpoison_string(reinterpret_cast(args.args[0])); #endif return broker_process->Open(reinterpret_cast(args.args[0]), static_cast(args.args[1])); case __NR_openat: // Allow using openat() as open(). if (static_cast(args.args[0]) == AT_FDCWD) { return broker_process->Open(reinterpret_cast(args.args[1]), static_cast(args.args[2])); } else { return -EPERM; } default: RAW_CHECK(false); return -ENOSYS; } } class GpuBrokerProcessPolicy : public GpuProcessPolicy { public: static sandbox::bpf_dsl::Policy* Create() { return new GpuBrokerProcessPolicy(); } virtual ~GpuBrokerProcessPolicy() {} virtual ResultExpr EvaluateSyscall(int system_call_number) const override; private: GpuBrokerProcessPolicy() {} DISALLOW_COPY_AND_ASSIGN(GpuBrokerProcessPolicy); }; // x86_64/i386 or desktop ARM. // A GPU broker policy is the same as a GPU policy with open and // openat allowed. ResultExpr GpuBrokerProcessPolicy::EvaluateSyscall(int sysno) const { switch (sysno) { case __NR_access: case __NR_open: case __NR_openat: return Allow(); default: return GpuProcessPolicy::EvaluateSyscall(sysno); } } void UpdateProcessTypeToGpuBroker() { base::CommandLine::StringVector exec = base::CommandLine::ForCurrentProcess()->GetArgs(); base::CommandLine::Reset(); base::CommandLine::Init(0, NULL); base::CommandLine::ForCurrentProcess()->InitFromArgv(exec); base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kProcessType, "gpu-broker"); // Update the process title. The argv was already cached by the call to // SetProcessTitleFromCommandLine in content_main_runner.cc, so we can pass // NULL here (we don't have the original argv at this point). SetProcessTitleFromCommandLine(NULL); } bool UpdateProcessTypeAndEnableSandbox( sandbox::bpf_dsl::Policy* (*broker_sandboxer_allocator)(void)) { DCHECK(broker_sandboxer_allocator); UpdateProcessTypeToGpuBroker(); return SandboxSeccompBPF::StartSandboxWithExternalPolicy( make_scoped_ptr(broker_sandboxer_allocator())); } } // namespace GpuProcessPolicy::GpuProcessPolicy() : GpuProcessPolicy(false) { } GpuProcessPolicy::GpuProcessPolicy(bool allow_mincore) : broker_process_(NULL), allow_mincore_(allow_mincore) { } GpuProcessPolicy::~GpuProcessPolicy() {} // Main policy for x86_64/i386. Extended by CrosArmGpuProcessPolicy. ResultExpr GpuProcessPolicy::EvaluateSyscall(int sysno) const { switch (sysno) { case __NR_ioctl: return Allow(); case __NR_mincore: if (allow_mincore_) { return Allow(); } else { return SandboxBPFBasePolicy::EvaluateSyscall(sysno); } #if defined(__i386__) || defined(__x86_64__) || defined(__mips__) // The Nvidia driver uses flags not in the baseline policy // (MAP_LOCKED | MAP_EXECUTABLE | MAP_32BIT) case __NR_mmap: #endif // We also hit this on the linux_chromeos bot but don't yet know what // weird flags were involved. case __NR_mprotect: // TODO(jln): restrict prctl. case __NR_prctl: return Allow(); case __NR_access: case __NR_open: case __NR_openat: DCHECK(broker_process_); return Trap(GpuSIGSYS_Handler, broker_process_); case __NR_setpriority: return sandbox::RestrictGetSetpriority(GetPolicyPid()); case __NR_sched_getaffinity: case __NR_sched_setaffinity: return sandbox::RestrictSchedTarget(GetPolicyPid(), sysno); default: if (SyscallSets::IsEventFd(sysno)) return Allow(); // Default on the baseline policy. return SandboxBPFBasePolicy::EvaluateSyscall(sysno); } } bool GpuProcessPolicy::PreSandboxHook() { // Warm up resources needed by the policy we're about to enable and // eventually start a broker process. const bool chromeos_arm_gpu = IsChromeOS() && IsArchitectureArm(); // This policy is for x86 or Desktop. DCHECK(!chromeos_arm_gpu); DCHECK(!broker_process()); // Create a new broker process. InitGpuBrokerProcess( GpuBrokerProcessPolicy::Create, std::vector(), // No extra files in whitelist. std::vector()); if (IsArchitectureX86_64() || IsArchitectureI386()) { // Accelerated video dlopen()'s some shared objects // inside the sandbox, so preload them now. if (IsAcceleratedVideoEnabled()) { const char* I965DrvVideoPath = NULL; if (IsArchitectureX86_64()) { I965DrvVideoPath = "/usr/lib64/va/drivers/i965_drv_video.so"; } else if (IsArchitectureI386()) { I965DrvVideoPath = "/usr/lib/va/drivers/i965_drv_video.so"; } dlopen(I965DrvVideoPath, RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE); dlopen("libva.so.1", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE); dlopen("libva-x11.so.1", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE); } } return true; } void GpuProcessPolicy::InitGpuBrokerProcess( sandbox::bpf_dsl::Policy* (*broker_sandboxer_allocator)(void), const std::vector& read_whitelist_extra, const std::vector& write_whitelist_extra) { static const char kDriRcPath[] = "/etc/drirc"; static const char kDriCard0Path[] = "/dev/dri/card0"; CHECK(broker_process_ == NULL); // All GPU process policies need these files brokered out. std::vector read_whitelist; read_whitelist.push_back(kDriCard0Path); read_whitelist.push_back(kDriRcPath); // Add eventual extra files from read_whitelist_extra. read_whitelist.insert(read_whitelist.end(), read_whitelist_extra.begin(), read_whitelist_extra.end()); std::vector write_whitelist; write_whitelist.push_back(kDriCard0Path); // Add eventual extra files from write_whitelist_extra. write_whitelist.insert(write_whitelist.end(), write_whitelist_extra.begin(), write_whitelist_extra.end()); broker_process_ = new BrokerProcess(GetFSDeniedErrno(), read_whitelist, write_whitelist); // The initialization callback will perform generic initialization and then // call broker_sandboxer_callback. CHECK(broker_process_->Init(base::Bind(&UpdateProcessTypeAndEnableSandbox, broker_sandboxer_allocator))); } } // namespace content