Index: content/common/sandbox_init_linux.cc |
diff --git a/content/common/sandbox_init_linux.cc b/content/common/sandbox_init_linux.cc |
index b9cafa2f2bcb702ce54b7bcf4adc97538cf0e965..46db79573c30dfc12e7d2fe667c4ff8421518ee8 100644 |
--- a/content/common/sandbox_init_linux.cc |
+++ b/content/common/sandbox_init_linux.cc |
@@ -2,574 +2,47 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include "content/common/seccomp_sandbox.h" |
-#include "content/public/common/sandbox_init.h" |
- |
-#if defined(__i386__) || defined(__x86_64__) |
- |
-// This is an assert for GYP |
-#if !defined(OS_LINUX) |
- #error "Linux specific file compiled on non Linux OS!" |
-#endif |
- |
-#include <asm/unistd.h> |
-#include <dlfcn.h> |
-#include <errno.h> |
-#include <fcntl.h> |
-#include <linux/audit.h> |
-#include <linux/filter.h> |
-#include <signal.h> |
-#include <string.h> |
-#include <sys/prctl.h> |
-#include <sys/stat.h> |
-#include <sys/types.h> |
-#include <ucontext.h> |
-#include <unistd.h> |
- |
-#include <vector> |
+#include <string> |
#include "base/command_line.h" |
-#include "base/file_util.h" |
#include "base/logging.h" |
-#include "base/time.h" |
#include "content/common/sandbox_linux.h" |
#include "content/public/common/content_switches.h" |
-#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" |
- |
-// These are fairly new and not defined in all headers yet. |
-#if defined(__x86_64__) |
- |
-#ifndef __NR_process_vm_readv |
- #define __NR_process_vm_readv 310 |
-#endif |
- |
-#ifndef __NR_process_vm_writev |
- #define __NR_process_vm_writev 311 |
-#endif |
- |
-#elif defined(__i386__) |
- |
-#ifndef __NR_process_vm_readv |
- #define __NR_process_vm_readv 347 |
-#endif |
- |
-#ifndef __NR_process_vm_writev |
- #define __NR_process_vm_writev 348 |
-#endif |
- |
-#endif |
- |
-namespace { |
- |
-bool IsSingleThreaded() { |
- // Possibly racy, but it's ok because this is more of a debug check to catch |
- // new threaded situations arising during development. |
- int num_threads = |
- file_util::CountFilesCreatedAfter(FilePath("/proc/self/task"), |
- base::Time::UnixEpoch()); |
- |
- // We pass the test if we don't know ( == 0), because the setuid sandbox |
- // will prevent /proc access in some contexts. |
- return num_threads == 1 || num_threads == 0; |
-} |
- |
-inline bool IsChromeOS() { |
-#if defined(OS_CHROMEOS) |
- return true; |
-#else |
- return false; |
-#endif |
-} |
- |
-void LogSandboxStarted(const std::string& sandbox_name, |
- const std::string& process_type) { |
- const std::string activated_sandbox = |
- "Activated " + sandbox_name + " sandbox for process type: " + |
- process_type + "."; |
- if (IsChromeOS()) { |
- LOG(WARNING) << activated_sandbox; |
- } else { |
- VLOG(1) << activated_sandbox; |
- } |
-} |
- |
-intptr_t CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux) { |
- int syscall = args.nr; |
- if (syscall >= 1024) |
- syscall = 0; |
- // Encode 8-bits of the 1st two arguments too, so we can discern which socket |
- // type, which fcntl, ... etc., without being likely to hit a mapped |
- // address. |
- // Do not encode more bits here without thinking about increasing the |
- // likelihood of collision with mapped pages. |
- syscall |= ((args.args[0] & 0xffUL) << 12); |
- syscall |= ((args.args[1] & 0xffUL) << 20); |
- // Purposefully dereference the syscall as an address so it'll show up very |
- // clearly and easily in crash dumps. |
- volatile char* addr = reinterpret_cast<volatile char*>(syscall); |
- *addr = '\0'; |
- // In case we hit a mapped address, hit the null page with just the syscall, |
- // for paranoia. |
- syscall &= 0xfffUL; |
- addr = reinterpret_cast<volatile char*>(syscall); |
- *addr = '\0'; |
- for (;;) |
- _exit(1); |
-} |
- |
-// TODO(jln) we need to restrict the first parameter! |
-bool IsKillSyscall(int sysno) { |
- switch (sysno) { |
- case __NR_kill: |
- case __NR_tkill: |
- case __NR_tgkill: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool IsGettimeSyscall(int sysno) { |
- switch (sysno) { |
- case __NR_clock_gettime: |
- case __NR_gettimeofday: |
- case __NR_time: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool IsFileSystemSyscall(int sysno) { |
- switch (sysno) { |
- case __NR_open: |
- case __NR_openat: |
- case __NR_execve: |
- case __NR_access: |
- case __NR_mkdir: |
- case __NR_mkdirat: |
- case __NR_readlink: |
- case __NR_readlinkat: |
- case __NR_stat: |
- case __NR_lstat: |
- case __NR_chdir: |
- case __NR_mknod: |
- case __NR_mknodat: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool IsAcceleratedVideoDecodeEnabled() { |
- // Accelerated video decode is currently enabled on Chrome OS, |
- // but not on Linux: crbug.com/137247. |
- bool is_enabled = IsChromeOS(); |
- |
- const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
- is_enabled = is_enabled && |
- !command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode); |
- |
- return is_enabled; |
-} |
- |
-static const char kDriRcPath[] = "/etc/drirc"; |
- |
-// TODO(jorgelo): limited to /etc/drirc for now, extend this to cover |
-// other sandboxed file access cases. |
-int OpenWithCache(const char* pathname, int flags) { |
- static int drircfd = -1; |
- static bool do_open = true; |
- int res = -1; |
- |
- if (strcmp(pathname, kDriRcPath) == 0 && flags == O_RDONLY) { |
- if (do_open) { |
- drircfd = open(pathname, flags); |
- do_open = false; |
- res = drircfd; |
- } else { |
- // dup() man page: |
- // "After a successful return from one of these system calls, |
- // the old and new file descriptors may be used interchangeably. |
- // They refer to the same open file description and thus share |
- // file offset and file status flags; for example, if the file offset |
- // is modified by using lseek(2) on one of the descriptors, |
- // the offset is also changed for the other." |
- // Since |drircfd| can be dup()'ed and read many times, we need to |
- // lseek() it to the beginning of the file before returning. |
- // We assume the caller will not keep more than one fd open at any |
- // one time. Intel driver code in Mesa that parses /etc/drirc does |
- // open()/read()/close() in the same function. |
- if (drircfd < 0) { |
- errno = ENOENT; |
- return -1; |
- } |
- int newfd = dup(drircfd); |
- if (newfd < 0) { |
- errno = ENOMEM; |
- return -1; |
- } |
- if (lseek(newfd, 0, SEEK_SET) == static_cast<off_t>(-1)) { |
- (void) HANDLE_EINTR(close(newfd)); |
- errno = ENOMEM; |
- return -1; |
- } |
- res = newfd; |
- } |
- } else { |
- res = open(pathname, flags); |
- } |
- |
- return res; |
-} |
- |
-// We allow the GPU process to open /etc/drirc because it's needed by Mesa. |
-// OpenWithCache() has been called before enabling the sandbox, and has cached |
-// a file descriptor for /etc/drirc. |
-intptr_t GpuOpenSIGSYS_Handler(const struct arch_seccomp_data& args, |
- void* aux) { |
- uint64_t arg0 = args.args[0]; |
- uint64_t arg1 = args.args[1]; |
- const char* pathname = reinterpret_cast<const char*>(arg0); |
- int flags = static_cast<int>(arg1); |
- |
- if (strcmp(pathname, kDriRcPath) == 0) { |
- int ret = OpenWithCache(pathname, flags); |
- return (ret == -1) ? -errno : ret; |
- } else { |
- return -ENOENT; |
- } |
-} |
- |
-#if defined(__x86_64__) |
-// x86_64 only because it references system calls that are multiplexed on IA32. |
-playground2::Sandbox::ErrorCode GpuProcessPolicy_x86_64(int sysno) { |
- switch(sysno) { |
- case __NR_read: |
- case __NR_ioctl: |
- case __NR_poll: |
- case __NR_epoll_wait: |
- case __NR_recvfrom: |
- case __NR_write: |
- case __NR_writev: |
- case __NR_gettid: |
- case __NR_sched_yield: // Nvidia binary driver. |
- |
- case __NR_futex: |
- case __NR_madvise: |
- case __NR_sendmsg: |
- case __NR_recvmsg: |
- case __NR_eventfd2: |
- case __NR_pipe: |
- case __NR_mmap: |
- case __NR_mprotect: |
- case __NR_clone: // TODO(jln) restrict flags. |
- case __NR_set_robust_list: |
- case __NR_getuid: |
- case __NR_geteuid: |
- case __NR_getgid: |
- case __NR_getegid: |
- case __NR_epoll_create: |
- case __NR_fcntl: |
- case __NR_socketpair: |
- case __NR_epoll_ctl: |
- case __NR_prctl: |
- case __NR_fstat: |
- case __NR_close: |
- case __NR_restart_syscall: |
- case __NR_rt_sigreturn: |
- case __NR_brk: |
- case __NR_rt_sigprocmask: |
- case __NR_munmap: |
- case __NR_dup: |
- case __NR_mlock: |
- case __NR_munlock: |
- case __NR_exit: |
- case __NR_exit_group: |
- case __NR_lseek: |
- case __NR_getpid: // Nvidia binary driver. |
- case __NR_getppid: // ATI binary driver. |
- case __NR_shutdown: // Virtual driver. |
- case __NR_rt_sigaction: // Breakpad signal handler. |
- return playground2::Sandbox::SB_ALLOWED; |
- case __NR_socket: |
- return EACCES; // Nvidia binary driver. |
- case __NR_fchmod: |
- return EPERM; // ATI binary driver. |
- case __NR_open: |
- // Accelerated video decode is enabled by default only on Chrome OS. |
- if (IsAcceleratedVideoDecodeEnabled()) { |
- // Accelerated video decode needs to open /dev/dri/card0, and |
- // dup()'ing an already open file descriptor does not work. |
- // Allow open() even though it severely weakens the sandbox, |
- // to test the sandboxing mechanism in general. |
- // TODO(jorgelo): remove this once we solve the libva issue. |
- return playground2::Sandbox::SB_ALLOWED; |
- } else { |
- // Hook open() in the GPU process to allow opening /etc/drirc, |
- // needed by Mesa. |
- // The hook needs dup(), lseek(), and close() to be allowed. |
- return playground2::Sandbox::ErrorCode(GpuOpenSIGSYS_Handler, NULL); |
- } |
- default: |
- if (IsGettimeSyscall(sysno) || |
- IsKillSyscall(sysno)) { // GPU watchdog. |
- return playground2::Sandbox::SB_ALLOWED; |
- } |
- // Generally, filename-based syscalls will fail with ENOENT to behave |
- // similarly to a possible future setuid sandbox. |
- if (IsFileSystemSyscall(sysno)) { |
- return ENOENT; |
- } |
- // In any other case crash the program with our SIGSYS handler |
- return playground2::Sandbox::ErrorCode(CrashSIGSYS_Handler, NULL); |
- } |
-} |
- |
-// x86_64 only because it references system calls that are multiplexed on IA32. |
-playground2::Sandbox::ErrorCode FlashProcessPolicy_x86_64(int sysno) { |
- switch (sysno) { |
- case __NR_futex: |
- case __NR_write: |
- case __NR_epoll_wait: |
- case __NR_read: |
- case __NR_times: |
- case __NR_clone: // TODO(jln): restrict flags. |
- case __NR_set_robust_list: |
- case __NR_getuid: |
- case __NR_geteuid: |
- case __NR_getgid: |
- case __NR_getegid: |
- case __NR_epoll_create: |
- case __NR_fcntl: |
- case __NR_socketpair: |
- case __NR_pipe: |
- case __NR_epoll_ctl: |
- case __NR_gettid: |
- case __NR_prctl: |
- case __NR_fstat: |
- case __NR_sendmsg: |
- case __NR_mmap: |
- case __NR_munmap: |
- case __NR_mprotect: |
- case __NR_madvise: |
- case __NR_rt_sigaction: |
- case __NR_rt_sigprocmask: |
- case __NR_wait4: |
- case __NR_exit_group: |
- case __NR_exit: |
- case __NR_rt_sigreturn: |
- case __NR_restart_syscall: |
- case __NR_close: |
- case __NR_recvmsg: |
- case __NR_lseek: |
- case __NR_brk: |
- case __NR_sched_yield: |
- case __NR_shutdown: |
- case __NR_sched_getaffinity: |
- case __NR_sched_setscheduler: |
- case __NR_dup: // Flash Access. |
- // These are under investigation, and hopefully not here for the long term. |
- case __NR_shmctl: |
- case __NR_shmat: |
- case __NR_shmdt: |
- return playground2::Sandbox::SB_ALLOWED; |
- case __NR_ioctl: |
- return ENOTTY; // Flash Access. |
- case __NR_socket: |
- return EACCES; |
- default: |
- if (IsGettimeSyscall(sysno) || |
- IsKillSyscall(sysno)) { |
- return playground2::Sandbox::SB_ALLOWED; |
- } |
- if (IsFileSystemSyscall(sysno)) { |
- return ENOENT; |
- } |
- // In any other case crash the program with our SIGSYS handler. |
- return playground2::Sandbox::ErrorCode(CrashSIGSYS_Handler, NULL); |
- } |
-} |
-#endif |
- |
-playground2::Sandbox::ErrorCode BlacklistPtracePolicy(int sysno) { |
- if (sysno < static_cast<int>(MIN_SYSCALL) || |
- sysno > static_cast<int>(MAX_SYSCALL)) { |
- // TODO(jln) we should not have to do that in a trivial policy. |
- return ENOSYS; |
- } |
- switch (sysno) { |
- case __NR_ptrace: |
- case __NR_process_vm_readv: |
- case __NR_process_vm_writev: |
- case __NR_migrate_pages: |
- case __NR_move_pages: |
- return playground2::Sandbox::ErrorCode(CrashSIGSYS_Handler, NULL); |
- default: |
- return playground2::Sandbox::SB_ALLOWED; |
- } |
-} |
- |
-// Allow all syscalls. |
-// This will still deny x32 or IA32 calls in 64 bits mode or |
-// 64 bits system calls in compatibility mode. |
-playground2::Sandbox::ErrorCode AllowAllPolicy(int sysno) { |
- if (sysno < static_cast<int>(MIN_SYSCALL) || |
- sysno > static_cast<int>(MAX_SYSCALL)) { |
- // TODO(jln) we should not have to do that in a trivial policy. |
- return ENOSYS; |
- } else { |
- return playground2::Sandbox::SB_ALLOWED; |
- } |
-} |
- |
-// Warms up/preloads resources needed by the policies. |
-void WarmupPolicy(playground2::Sandbox::EvaluateSyscall policy) { |
-#if defined(__x86_64__) |
- if (policy == GpuProcessPolicy_x86_64) { |
- OpenWithCache(kDriRcPath, O_RDONLY); |
- // Accelerated video decode dlopen()'s this shared object |
- // inside the sandbox, so preload it now. |
- // TODO(jorgelo): generalize this to other platforms. |
- if (IsAcceleratedVideoDecodeEnabled()) { |
- const char kI965DrvVideoPath_64[] = |
- "/usr/lib64/va/drivers/i965_drv_video.so"; |
- dlopen(kI965DrvVideoPath_64, RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE); |
- } |
- } |
-#endif |
-} |
- |
-// Is the sandbox fully disabled for this process? |
-bool ShouldDisableBpfSandbox(const CommandLine& command_line, |
- const std::string& process_type) { |
- if (command_line.HasSwitch(switches::kNoSandbox) || |
- command_line.HasSwitch(switches::kDisableSeccompFilterSandbox)) { |
- return true; |
- } |
- |
- if (process_type == switches::kGpuProcess) { |
- // The GPU sandbox is disabled by default in ChromeOS, enabled by default on |
- // generic Linux. |
- // TODO(jorgelo): when we feel comfortable, make this a policy decision |
- // instead. (i.e. move this to GetProcessSyscallPolicy) and return an |
- // AllowAllPolicy for lack of "--enable-gpu-sandbox". |
- bool should_disable; |
- if (IsChromeOS()) { |
- should_disable = true; |
- } else { |
- should_disable = false; |
- } |
- |
- if (command_line.HasSwitch(switches::kEnableGpuSandbox)) |
- should_disable = false; |
- if (command_line.HasSwitch(switches::kDisableGpuSandbox)) |
- should_disable = true; |
- return should_disable; |
- } |
- |
- return false; |
-} |
- |
-playground2::Sandbox::EvaluateSyscall GetProcessSyscallPolicy( |
- const CommandLine& command_line, |
- const std::string& process_type) { |
-#if defined(__x86_64__) |
- if (process_type == switches::kGpuProcess) { |
- return GpuProcessPolicy_x86_64; |
- } |
+#include "content/public/common/sandbox_init.h" |
- if (process_type == switches::kPpapiPluginProcess) { |
- // TODO(jln): figure out what to do with non-Flash PPAPI |
- // out-of-process plug-ins. |
- return FlashProcessPolicy_x86_64; |
- } |
+namespace content { |
- if (process_type == switches::kRendererProcess || |
- process_type == switches::kWorkerProcess) { |
- return BlacklistPtracePolicy; |
- } |
- NOTREACHED(); |
- // This will be our default if we need one. |
- return AllowAllPolicy; |
-#else |
- // On IA32, we only have a small blacklist at the moment. |
- (void) process_type; |
- return BlacklistPtracePolicy; |
-#endif // __x86_64__ |
-} |
+// TODO(jln): have call sites provide a process / policy type to |
+// InitializeSandbox(). |
+void InitializeSandbox() { |
+ bool seccomp_legacy_started = false; |
+ LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance(); |
+ const std::string process_type = |
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
+ switches::kProcessType); |
piman
2012/08/02 21:40:04
nit: indent (switches should be at +4)
jln (very slow on Chromium)
2012/08/02 22:20:38
Done.
|
-// Initialize the seccomp-bpf sandbox. |
-bool InitializeBpfSandbox_x86(const CommandLine& command_line, |
- const std::string& process_type) { |
- if (ShouldDisableBpfSandbox(command_line, process_type)) |
- return false; |
- // No matter what, InitializeSandbox() should always be called before threads |
- // are started. |
- // Note: IsSingleThreaded() will be true if /proc is not accessible! |
- if (!IsSingleThreaded()) { |
+ // No matter what, it's always an error to call InitializeSandbox() after |
+ // threads have been created. |
+ if (!linux_sandbox->IsSingleThreaded()) { |
std::string error_message = "InitializeSandbox() called with multiple " |
"threads in process " + process_type; |
// TODO(jln): change this into a CHECK() once we are more comfortable it |
// does not trigger. |
- // On non-DEBUG build, we still log an error |
LOG(ERROR) << error_message; |
- return false; |
- } |
- |
- // TODO(jln): find a way for the Zygote processes under the setuid sandbox to |
- // have a /proc fd and pass it here. |
- // Passing -1 as the /proc fd since we have no special way to have it for |
- // now. |
- if (playground2::Sandbox::supportsSeccompSandbox(-1) != |
- playground2::Sandbox::STATUS_AVAILABLE) { |
- return false; |
+ return; |
} |
- playground2::Sandbox::EvaluateSyscall SyscallPolicy = |
- GetProcessSyscallPolicy(command_line, process_type); |
- |
- // Warms up resources needed by the policy we're about to enable. |
- WarmupPolicy(SyscallPolicy); |
- |
- playground2::Sandbox::setSandboxPolicy(SyscallPolicy, NULL); |
- playground2::Sandbox::startSandbox(); |
- |
- return true; |
-} |
- |
-} // anonymous namespace |
- |
-#endif // defined(__i386__) || defined(__x86_64__) |
- |
-namespace content { |
- |
-void InitializeSandbox() { |
-#if defined(__i386__) || defined(__x86_64__) |
- const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
- const std::string process_type = |
- command_line.GetSwitchValueASCII(switches::kProcessType); |
- bool seccomp_legacy_started = false; |
- bool seccomp_bpf_started = false; |
- |
// First, try to enable seccomp-legacy. |
- seccomp_legacy_started = |
- LinuxSandbox::GetInstance()->StartSeccompLegacy(process_type); |
- if (seccomp_legacy_started) |
- LogSandboxStarted("seccomp-legacy", process_type); |
+ seccomp_legacy_started = linux_sandbox->StartSeccompLegacy(process_type); |
// Then, try to enable seccomp-bpf. |
// If seccomp-legacy is enabled, seccomp-bpf initialization will crash |
// instead of failing gracefully. |
// TODO(markus): fix this (crbug.com/139872). |
if (!seccomp_legacy_started) { |
- seccomp_bpf_started = |
- InitializeBpfSandbox_x86(command_line, process_type); |
+ linux_sandbox->StartSeccompBpf(process_type); |
} |
- if (seccomp_bpf_started) |
- LogSandboxStarted("seccomp-bpf", process_type); |
-#endif |
} |
} // namespace content |