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 "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h" 6 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <sys/socket.h> 10 #include <sys/stat.h> 11 #include <sys/stat.h> 12 #include <sys/types.h> 13 #include <sys/types.h> 14 15 #include "base/basictypes.h" 16 #include "base/command_line.h" 17 #include "base/logging.h" 18 #include "build/build_config.h" 19 #include "content/public/common/content_switches.h" 20 21 // These are the only architectures supported for now. 22 #if defined(__i386__) || defined(__x86_64__) || \ 23 (defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))) 24 #define SECCOMP_BPF_SANDBOX 25 #endif 26 27 #if defined(SECCOMP_BPF_SANDBOX) 28 #include "base/posix/eintr_wrapper.h" 29 #include "content/common/sandbox_linux/bpf_cros_arm_gpu_policy_linux.h" 30 #include "content/common/sandbox_linux/bpf_gpu_policy_linux.h" 31 #include "content/common/sandbox_linux/bpf_ppapi_policy_linux.h" 32 #include "content/common/sandbox_linux/bpf_renderer_policy_linux.h" 33 #include "content/common/sandbox_linux/sandbox_bpf_base_policy_linux.h" 34 #include "content/common/sandbox_linux/sandbox_linux.h" 35 #include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h" 36 #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" 37 #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" 38 #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" 39 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" 40 #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" 41 #include "sandbox/linux/services/linux_syscalls.h" 42 43 using sandbox::BaselinePolicy; 44 using sandbox::SyscallSets; 45 46 namespace content { 47 48 namespace { 49 50 void StartSandboxWithPolicy(sandbox::SandboxBPFPolicy* policy); 51 52 inline bool IsChromeOS() { 53 #if defined(OS_CHROMEOS) 54 return true; 55 #else 56 return false; 57 #endif 58 } 59 60 inline bool IsArchitectureArm() { 61 #if defined(__arm__) 62 return true; 63 #else 64 return false; 65 #endif 66 } 67 68 class BlacklistDebugAndNumaPolicy : public SandboxBPFBasePolicy { 69 public: 70 BlacklistDebugAndNumaPolicy() {} 71 virtual ~BlacklistDebugAndNumaPolicy() {} 72 73 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, 74 int system_call_number) const OVERRIDE; 75 76 private: 77 DISALLOW_COPY_AND_ASSIGN(BlacklistDebugAndNumaPolicy); 78 }; 79 80 ErrorCode BlacklistDebugAndNumaPolicy::EvaluateSyscall(SandboxBPF* sandbox, 81 int sysno) const { 82 if (!SandboxBPF::IsValidSyscallNumber(sysno)) { 83 // TODO(jln) we should not have to do that in a trivial policy. 84 return ErrorCode(ENOSYS); 85 } 86 if (SyscallSets::IsDebug(sysno) || SyscallSets::IsNuma(sysno)) 87 return sandbox->Trap(sandbox::CrashSIGSYS_Handler, NULL); 88 89 return ErrorCode(ErrorCode::ERR_ALLOWED); 90 } 91 92 class AllowAllPolicy : public SandboxBPFBasePolicy { 93 public: 94 AllowAllPolicy() {} 95 virtual ~AllowAllPolicy() {} 96 97 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, 98 int system_call_number) const OVERRIDE; 99 100 private: 101 DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy); 102 }; 103 104 // Allow all syscalls. 105 // This will still deny x32 or IA32 calls in 64 bits mode or 106 // 64 bits system calls in compatibility mode. 107 ErrorCode AllowAllPolicy::EvaluateSyscall(SandboxBPF*, int sysno) const { 108 if (!SandboxBPF::IsValidSyscallNumber(sysno)) { 109 // TODO(jln) we should not have to do that in a trivial policy. 110 return ErrorCode(ENOSYS); 111 } else { 112 return ErrorCode(ErrorCode::ERR_ALLOWED); 113 } 114 } 115 116 // If a BPF policy is engaged for |process_type|, run a few sanity checks. 117 void RunSandboxSanityChecks(const std::string& process_type) { 118 if (process_type == switches::kRendererProcess || 119 process_type == switches::kWorkerProcess || 120 process_type == switches::kGpuProcess || 121 process_type == switches::kPpapiPluginProcess) { 122 int syscall_ret; 123 errno = 0; 124 125 // Without the sandbox, this would EBADF. 126 syscall_ret = fchmod(-1, 07777); 127 CHECK_EQ(-1, syscall_ret); 128 CHECK_EQ(EPERM, errno); 129 130 // Run most of the sanity checks only in DEBUG mode to avoid a perf. 131 // impact. 132 #if !defined(NDEBUG) 133 // open() must be restricted. 134 syscall_ret = open("/etc/passwd", O_RDONLY); 135 CHECK_EQ(-1, syscall_ret); 136 CHECK_EQ(SandboxBPFBasePolicy::GetFSDeniedErrno(), errno); 137 138 // We should never allow the creation of netlink sockets. 139 syscall_ret = socket(AF_NETLINK, SOCK_DGRAM, 0); 140 CHECK_EQ(-1, syscall_ret); 141 CHECK_EQ(EPERM, errno); 142 #endif // !defined(NDEBUG) 143 } 144 } 145 146 147 // This function takes ownership of |policy|. 148 void StartSandboxWithPolicy(sandbox::SandboxBPFPolicy* policy) { 149 // Starting the sandbox is a one-way operation. The kernel doesn't allow 150 // us to unload a sandbox policy after it has been started. Nonetheless, 151 // in order to make the use of the "Sandbox" object easier, we allow for 152 // the object to be destroyed after the sandbox has been started. Note that 153 // doing so does not stop the sandbox. 154 SandboxBPF sandbox; 155 sandbox.SetSandboxPolicy(policy); 156 sandbox.StartSandbox(); 157 } 158 159 // nacl_helper needs to be tiny and includes only part of content/ 160 // in its dependencies. Make sure to not link things that are not needed. 161 #if !defined(IN_NACL_HELPER) 162 scoped_ptr<SandboxBPFBasePolicy> GetGpuProcessSandbox() { 163 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 164 bool allow_sysv_shm = false; 165 if (command_line.HasSwitch(switches::kGpuSandboxAllowSysVShm)) { 166 DCHECK(IsArchitectureArm()); 167 allow_sysv_shm = true; 168 } 169 170 if (IsChromeOS() && IsArchitectureArm()) { 171 return scoped_ptr<SandboxBPFBasePolicy>( 172 new CrosArmGpuProcessPolicy(allow_sysv_shm)); 173 } else { 174 return scoped_ptr<SandboxBPFBasePolicy>(new GpuProcessPolicy); 175 } 176 } 177 178 // Initialize the seccomp-bpf sandbox. 179 bool StartBPFSandbox(const CommandLine& command_line, 180 const std::string& process_type) { 181 scoped_ptr<SandboxBPFBasePolicy> policy; 182 183 if (process_type == switches::kGpuProcess) { 184 policy.reset(GetGpuProcessSandbox().release()); 185 } else if (process_type == switches::kRendererProcess || 186 process_type == switches::kWorkerProcess) { 187 policy.reset(new RendererProcessPolicy); 188 } else if (process_type == switches::kPpapiPluginProcess) { 189 policy.reset(new PpapiProcessPolicy); 190 } else if (process_type == switches::kUtilityProcess) { 191 policy.reset(new BlacklistDebugAndNumaPolicy); 192 } else { 193 NOTREACHED(); 194 policy.reset(new AllowAllPolicy); 195 } 196 197 CHECK(policy->PreSandboxHook()); 198 StartSandboxWithPolicy(policy.release()); 199 200 RunSandboxSanityChecks(process_type); 201 return true; 202 } 203 #else // defined(IN_NACL_HELPER) 204 bool StartBPFSandbox(const CommandLine& command_line, 205 const std::string& process_type) { 206 NOTREACHED(); 207 // Avoid -Wunused-function with no-op code. 208 ignore_result(IsChromeOS); 209 ignore_result(IsArchitectureArm); 210 ignore_result(RunSandboxSanityChecks); 211 return false; 212 } 213 #endif // !defined(IN_NACL_HELPER) 214 215 } // namespace 216 217 #endif // SECCOMP_BPF_SANDBOX 218 219 // Is seccomp BPF globally enabled? 220 bool SandboxSeccompBPF::IsSeccompBPFDesired() { 221 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 222 if (!command_line.HasSwitch(switches::kNoSandbox) && 223 !command_line.HasSwitch(switches::kDisableSeccompFilterSandbox)) { 224 return true; 225 } else { 226 return false; 227 } 228 } 229 230 bool SandboxSeccompBPF::ShouldEnableSeccompBPF( 231 const std::string& process_type) { 232 #if defined(SECCOMP_BPF_SANDBOX) 233 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 234 if (process_type == switches::kGpuProcess) 235 return !command_line.HasSwitch(switches::kDisableGpuSandbox); 236 237 return true; 238 #endif // SECCOMP_BPF_SANDBOX 239 return false; 240 } 241 242 bool SandboxSeccompBPF::SupportsSandbox() { 243 #if defined(SECCOMP_BPF_SANDBOX) 244 // TODO(jln): pass the saved proc_fd_ from the LinuxSandbox singleton 245 // here. 246 SandboxBPF::SandboxStatus bpf_sandbox_status = 247 SandboxBPF::SupportsSeccompSandbox(-1); 248 // Kernel support is what we are interested in here. Other status 249 // such as STATUS_UNAVAILABLE (has threads) still indicate kernel support. 250 // We make this a negative check, since if there is a bug, we would rather 251 // "fail closed" (expect a sandbox to be available and try to start it). 252 if (bpf_sandbox_status != SandboxBPF::STATUS_UNSUPPORTED) { 253 return true; 254 } 255 #endif 256 return false; 257 } 258 259 bool SandboxSeccompBPF::StartSandbox(const std::string& process_type) { 260 #if defined(SECCOMP_BPF_SANDBOX) 261 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 262 263 if (IsSeccompBPFDesired() && // Global switches policy. 264 ShouldEnableSeccompBPF(process_type) && // Process-specific policy. 265 SupportsSandbox()) { 266 // If the kernel supports the sandbox, and if the command line says we 267 // should enable it, enable it or die. 268 bool started_sandbox = StartBPFSandbox(command_line, process_type); 269 CHECK(started_sandbox); 270 return true; 271 } 272 #endif 273 return false; 274 } 275 276 bool SandboxSeccompBPF::StartSandboxWithExternalPolicy( 277 scoped_ptr<sandbox::SandboxBPFPolicy> policy) { 278 #if defined(SECCOMP_BPF_SANDBOX) 279 if (IsSeccompBPFDesired() && SupportsSandbox()) { 280 CHECK(policy); 281 StartSandboxWithPolicy(policy.release()); 282 return true; 283 } 284 #endif // defined(SECCOMP_BPF_SANDBOX) 285 return false; 286 } 287 288 scoped_ptr<sandbox::SandboxBPFPolicy> 289 SandboxSeccompBPF::GetBaselinePolicy() { 290 #if defined(SECCOMP_BPF_SANDBOX) 291 return scoped_ptr<sandbox::SandboxBPFPolicy>(new BaselinePolicy); 292 #else 293 return scoped_ptr<sandbox::SandboxBPFPolicy>(); 294 #endif // defined(SECCOMP_BPF_SANDBOX) 295 } 296 297 } // namespace content 298