|  | // Copyright (c) 2012 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/sandbox_seccomp_bpf_linux.h" | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <sys/socket.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/command_line.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/macros.h" | 
|  | #include "build/build_config.h" | 
|  | #include "content/public/common/content_switches.h" | 
|  | #include "sandbox/linux/bpf_dsl/bpf_dsl.h" | 
|  | #include "sandbox/sandbox_features.h" | 
|  |  | 
|  | #if BUILDFLAG(USE_SECCOMP_BPF) | 
|  |  | 
|  | #include "base/files/scoped_file.h" | 
|  | #include "base/posix/eintr_wrapper.h" | 
|  | #include "content/common/sandbox_linux/bpf_cros_arm_gpu_policy_linux.h" | 
|  | #include "content/common/sandbox_linux/bpf_gpu_policy_linux.h" | 
|  | #include "content/common/sandbox_linux/bpf_ppapi_policy_linux.h" | 
|  | #include "content/common/sandbox_linux/bpf_renderer_policy_linux.h" | 
|  | #include "content/common/sandbox_linux/bpf_utility_policy_linux.h" | 
|  | #include "content/common/sandbox_linux/sandbox_bpf_base_policy_linux.h" | 
|  | #include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h" | 
|  | #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" | 
|  | #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" | 
|  | #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" | 
|  | #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" | 
|  | #include "sandbox/linux/system_headers/linux_syscalls.h" | 
|  |  | 
|  | #if !defined(IN_NACL_HELPER) | 
|  | #include "ui/gl/gl_switches.h" | 
|  | #endif  // !defined(IN_NACL_HELPER) | 
|  |  | 
|  | using sandbox::BaselinePolicy; | 
|  | using sandbox::SandboxBPF; | 
|  | using sandbox::SyscallSets; | 
|  | using sandbox::bpf_dsl::Allow; | 
|  | using sandbox::bpf_dsl::ResultExpr; | 
|  |  | 
|  | #else | 
|  |  | 
|  | // Make sure that seccomp-bpf does not get disabled by mistake. Also make sure | 
|  | // that we think twice about this when adding a new architecture. | 
|  | #if !defined(ARCH_CPU_ARM64) | 
|  | #error "Seccomp-bpf disabled on supported architecture!" | 
|  | #endif  // !defined(ARCH_CPU_ARM64) | 
|  |  | 
|  | #endif  // | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | #if BUILDFLAG(USE_SECCOMP_BPF) | 
|  | namespace { | 
|  |  | 
|  | // This function takes ownership of |policy|. | 
|  | void StartSandboxWithPolicy(sandbox::bpf_dsl::Policy* policy, | 
|  | base::ScopedFD proc_fd) { | 
|  | // Starting the sandbox is a one-way operation. The kernel doesn't allow | 
|  | // us to unload a sandbox policy after it has been started. Nonetheless, | 
|  | // in order to make the use of the "Sandbox" object easier, we allow for | 
|  | // the object to be destroyed after the sandbox has been started. Note that | 
|  | // doing so does not stop the sandbox. | 
|  | SandboxBPF sandbox(policy); | 
|  |  | 
|  | sandbox.SetProcFd(std::move(proc_fd)); | 
|  | CHECK(sandbox.StartSandbox(SandboxBPF::SeccompLevel::SINGLE_THREADED)); | 
|  | } | 
|  |  | 
|  | #if !defined(OS_NACL_NONSFI) | 
|  |  | 
|  | class BlacklistDebugAndNumaPolicy : public SandboxBPFBasePolicy { | 
|  | public: | 
|  | BlacklistDebugAndNumaPolicy() {} | 
|  | ~BlacklistDebugAndNumaPolicy() override {} | 
|  |  | 
|  | ResultExpr EvaluateSyscall(int system_call_number) const override; | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(BlacklistDebugAndNumaPolicy); | 
|  | }; | 
|  |  | 
|  | ResultExpr BlacklistDebugAndNumaPolicy::EvaluateSyscall(int sysno) const { | 
|  | if (SyscallSets::IsDebug(sysno) || SyscallSets::IsNuma(sysno)) | 
|  | return sandbox::CrashSIGSYS(); | 
|  |  | 
|  | return Allow(); | 
|  | } | 
|  |  | 
|  | class AllowAllPolicy : public SandboxBPFBasePolicy { | 
|  | public: | 
|  | AllowAllPolicy() {} | 
|  | ~AllowAllPolicy() override {} | 
|  |  | 
|  | ResultExpr EvaluateSyscall(int system_call_number) const override; | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy); | 
|  | }; | 
|  |  | 
|  | // Allow all syscalls. | 
|  | // This will still deny x32 or IA32 calls in 64 bits mode or | 
|  | // 64 bits system calls in compatibility mode. | 
|  | ResultExpr AllowAllPolicy::EvaluateSyscall(int sysno) const { | 
|  | return Allow(); | 
|  | } | 
|  |  | 
|  | // nacl_helper needs to be tiny and includes only part of content/ | 
|  | // in its dependencies. Make sure to not link things that are not needed. | 
|  | #if !defined(IN_NACL_HELPER) | 
|  | inline bool IsChromeOS() { | 
|  | #if defined(OS_CHROMEOS) | 
|  | return true; | 
|  | #else | 
|  | return false; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline bool IsArchitectureArm() { | 
|  | #if defined(__arm__) | 
|  | return true; | 
|  | #else | 
|  | return false; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | // If a BPF policy is engaged for |process_type|, run a few sanity checks. | 
|  | void RunSandboxSanityChecks(const std::string& process_type) { | 
|  | if (process_type == switches::kRendererProcess || | 
|  | process_type == switches::kGpuProcess || | 
|  | process_type == switches::kPpapiPluginProcess) { | 
|  | int syscall_ret; | 
|  | errno = 0; | 
|  |  | 
|  | // Without the sandbox, this would EBADF. | 
|  | syscall_ret = fchmod(-1, 07777); | 
|  | CHECK_EQ(-1, syscall_ret); | 
|  | CHECK_EQ(EPERM, errno); | 
|  |  | 
|  | // Run most of the sanity checks only in DEBUG mode to avoid a perf. | 
|  | // impact. | 
|  | #if !defined(NDEBUG) | 
|  | // open() must be restricted. | 
|  | syscall_ret = open("/etc/passwd", O_RDONLY); | 
|  | CHECK_EQ(-1, syscall_ret); | 
|  | CHECK_EQ(SandboxBPFBasePolicy::GetFSDeniedErrno(), errno); | 
|  |  | 
|  | // We should never allow the creation of netlink sockets. | 
|  | syscall_ret = socket(AF_NETLINK, SOCK_DGRAM, 0); | 
|  | CHECK_EQ(-1, syscall_ret); | 
|  | CHECK_EQ(EPERM, errno); | 
|  | #endif  // !defined(NDEBUG) | 
|  | } | 
|  | } | 
|  |  | 
|  | std::unique_ptr<SandboxBPFBasePolicy> GetGpuProcessSandbox() { | 
|  | const base::CommandLine& command_line = | 
|  | *base::CommandLine::ForCurrentProcess(); | 
|  | if (IsChromeOS() && IsArchitectureArm()) { | 
|  | bool allow_sysv_shm = | 
|  | command_line.HasSwitch(switches::kGpuSandboxAllowSysVShm); | 
|  | return std::unique_ptr<SandboxBPFBasePolicy>( | 
|  | new CrosArmGpuProcessPolicy(allow_sysv_shm)); | 
|  | } else { | 
|  | bool allow_mincore = command_line.HasSwitch(switches::kUseGL) && | 
|  | command_line.GetSwitchValueASCII(switches::kUseGL) == | 
|  | gl::kGLImplementationEGLName; | 
|  | return std::unique_ptr<SandboxBPFBasePolicy>( | 
|  | new GpuProcessPolicy(allow_mincore)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Initialize the seccomp-bpf sandbox. | 
|  | bool StartBPFSandbox(const base::CommandLine& command_line, | 
|  | const std::string& process_type, | 
|  | base::ScopedFD proc_fd) { | 
|  | std::unique_ptr<SandboxBPFBasePolicy> policy; | 
|  |  | 
|  | if (process_type == switches::kGpuProcess) { | 
|  | policy.reset(GetGpuProcessSandbox().release()); | 
|  | } else if (process_type == switches::kRendererProcess) { | 
|  | policy.reset(new RendererProcessPolicy); | 
|  | } else if (process_type == switches::kPpapiPluginProcess) { | 
|  | policy.reset(new PpapiProcessPolicy); | 
|  | } else if (process_type == switches::kUtilityProcess) { | 
|  | policy.reset(new UtilityProcessPolicy); | 
|  | } else { | 
|  | NOTREACHED(); | 
|  | policy.reset(new AllowAllPolicy); | 
|  | } | 
|  |  | 
|  | CHECK(policy->PreSandboxHook()); | 
|  | StartSandboxWithPolicy(policy.release(), std::move(proc_fd)); | 
|  |  | 
|  | RunSandboxSanityChecks(process_type); | 
|  | return true; | 
|  | } | 
|  | #else  // defined(IN_NACL_HELPER) | 
|  | bool StartBPFSandbox(const base::CommandLine& command_line, | 
|  | const std::string& process_type) { | 
|  | NOTREACHED(); | 
|  | return false; | 
|  | } | 
|  | #endif  // !defined(IN_NACL_HELPER) | 
|  | #endif  // !defined(OS_NACL_NONSFI) | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | #endif  // USE_SECCOMP_BPF | 
|  |  | 
|  | // Is seccomp BPF globally enabled? | 
|  | bool SandboxSeccompBPF::IsSeccompBPFDesired() { | 
|  | const base::CommandLine& command_line = | 
|  | *base::CommandLine::ForCurrentProcess(); | 
|  | if (!command_line.HasSwitch(switches::kNoSandbox) && | 
|  | !command_line.HasSwitch(switches::kDisableSeccompFilterSandbox)) { | 
|  | return true; | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | #if !defined(OS_NACL_NONSFI) | 
|  | bool SandboxSeccompBPF::ShouldEnableSeccompBPF( | 
|  | const std::string& process_type) { | 
|  | #if BUILDFLAG(USE_SECCOMP_BPF) | 
|  | const base::CommandLine& command_line = | 
|  | *base::CommandLine::ForCurrentProcess(); | 
|  | if (process_type == switches::kGpuProcess) | 
|  | return !command_line.HasSwitch(switches::kDisableGpuSandbox); | 
|  |  | 
|  | return true; | 
|  | #endif  // USE_SECCOMP_BPF | 
|  | return false; | 
|  | } | 
|  | #endif  // !defined(OS_NACL_NONSFI) | 
|  |  | 
|  | bool SandboxSeccompBPF::SupportsSandbox() { | 
|  | #if BUILDFLAG(USE_SECCOMP_BPF) | 
|  | return SandboxBPF::SupportsSeccompSandbox( | 
|  | SandboxBPF::SeccompLevel::SINGLE_THREADED); | 
|  | #endif | 
|  | return false; | 
|  | } | 
|  |  | 
|  | #if !defined(OS_NACL_NONSFI) | 
|  | bool SandboxSeccompBPF::SupportsSandboxWithTsync() { | 
|  | #if BUILDFLAG(USE_SECCOMP_BPF) | 
|  | return SandboxBPF::SupportsSeccompSandbox( | 
|  | SandboxBPF::SeccompLevel::MULTI_THREADED); | 
|  | #endif | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool SandboxSeccompBPF::StartSandbox(const std::string& process_type, | 
|  | base::ScopedFD proc_fd) { | 
|  | #if BUILDFLAG(USE_SECCOMP_BPF) | 
|  | const base::CommandLine& command_line = | 
|  | *base::CommandLine::ForCurrentProcess(); | 
|  |  | 
|  | if (IsSeccompBPFDesired() &&  // Global switches policy. | 
|  | ShouldEnableSeccompBPF(process_type) &&  // Process-specific policy. | 
|  | SupportsSandbox()) { | 
|  | // If the kernel supports the sandbox, and if the command line says we | 
|  | // should enable it, enable it or die. | 
|  | bool started_sandbox = | 
|  | StartBPFSandbox(command_line, process_type, std::move(proc_fd)); | 
|  | CHECK(started_sandbox); | 
|  | return true; | 
|  | } | 
|  | #endif | 
|  | return false; | 
|  | } | 
|  | #endif  // !defined(OS_NACL_NONSFI) | 
|  |  | 
|  | bool SandboxSeccompBPF::StartSandboxWithExternalPolicy( | 
|  | std::unique_ptr<sandbox::bpf_dsl::Policy> policy, | 
|  | base::ScopedFD proc_fd) { | 
|  | #if BUILDFLAG(USE_SECCOMP_BPF) | 
|  | if (IsSeccompBPFDesired() && SupportsSandbox()) { | 
|  | CHECK(policy); | 
|  | StartSandboxWithPolicy(policy.release(), std::move(proc_fd)); | 
|  | return true; | 
|  | } | 
|  | #endif  // BUILDFLAG(USE_SECCOMP_BPF) | 
|  | return false; | 
|  | } | 
|  |  | 
|  | #if !defined(OS_NACL_NONSFI) | 
|  | std::unique_ptr<sandbox::bpf_dsl::Policy> | 
|  | SandboxSeccompBPF::GetBaselinePolicy() { | 
|  | #if BUILDFLAG(USE_SECCOMP_BPF) | 
|  | return std::unique_ptr<sandbox::bpf_dsl::Policy>(new BaselinePolicy); | 
|  | #else | 
|  | return std::unique_ptr<sandbox::bpf_dsl::Policy>(); | 
|  | #endif  // BUILDFLAG(USE_SECCOMP_BPF) | 
|  | } | 
|  | #endif  // !defined(OS_NACL_NONSFI) | 
|  |  | 
|  | }  // namespace content |