Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(843)

Unified Diff: content/common/sandbox_init_linux.cc

Issue 10843042: Create a class for seccomp-bpf sandboxing in content. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase on top of tree. Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/browser/zygote_host/zygote_host_impl_linux.cc ('k') | content/common/sandbox_linux.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..bd2504b281a1ea5a508984d016d8cdb5e62d21f2 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);
-// 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
« no previous file with comments | « content/browser/zygote_host/zygote_host_impl_linux.cc ('k') | content/common/sandbox_linux.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698