Chromium Code Reviews| 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 |