summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorcevans@chromium.org <cevans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-13 21:08:51 +0000
committercevans@chromium.org <cevans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-13 21:08:51 +0000
commit2436a6b19ebb77226b96b0e37623b9b0785e172c (patch)
treee2967a8ae4f47dfd6a4620142bd79c75905d0e6f /content
parent5592bb5f24ebebd858d0f789b92aa87c5e420f5f (diff)
downloadchromium_src-2436a6b19ebb77226b96b0e37623b9b0785e172c.zip
chromium_src-2436a6b19ebb77226b96b0e37623b9b0785e172c.tar.gz
chromium_src-2436a6b19ebb77226b96b0e37623b9b0785e172c.tar.bz2
Add an initial Linux GPU sandbox using the seccomp filter framework.
Review URL: https://chromiumcodereview.appspot.com/10051022 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@132266 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r--content/common/sandbox_init_linux.cc263
-rw-r--r--content/content_common.gypi1
-rw-r--r--content/gpu/gpu_info_collector.cc4
-rw-r--r--content/gpu/gpu_main.cc17
-rw-r--r--content/public/common/sandbox_init.h2
5 files changed, 283 insertions, 4 deletions
diff --git a/content/common/sandbox_init_linux.cc b/content/common/sandbox_init_linux.cc
new file mode 100644
index 0000000..9605379
--- /dev/null
+++ b/content/common/sandbox_init_linux.cc
@@ -0,0 +1,263 @@
+// 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/public/common/sandbox_init.h"
+
+#if defined(OS_LINUX) && defined(__x86_64__)
+
+#include <asm/unistd.h>
+#include <errno.h>
+#include <linux/audit.h>
+#include <linux/filter.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/time.h"
+#include "content/public/common/content_switches.h"
+
+#ifndef PR_SET_NO_NEW_PRIVS
+ #define PR_SET_NO_NEW_PRIVS 38
+#endif
+
+#ifndef SYS_SECCOMP
+ #define SYS_SECCOMP 1
+#endif
+
+#ifndef __NR_eventfd2
+ #define __NR_eventfd2 290
+#endif
+
+// Constants from very new header files that we can't yet include.
+#ifndef SECCOMP_MODE_FILTER
+ #define SECCOMP_MODE_FILTER 2
+ #define SECCOMP_RET_KILL 0x00000000U
+ #define SECCOMP_RET_TRAP 0x00030000U
+ #define SECCOMP_RET_ERRNO 0x00050000U
+ #define SECCOMP_RET_ALLOW 0x7fff0000U
+#endif
+
+
+namespace {
+
+static void CheckSingleThreaded() {
+ int num_threads = file_util::CountFilesCreatedAfter(
+ FilePath("/proc/self/task"), base::Time::UnixEpoch());
+ // Possibly racy, but it's ok because this is more of a debug check to catch
+ // new threaded situations arising during development.
+ CHECK_EQ(num_threads, 1);
+}
+
+static void SIGSYS_Handler(int signal, siginfo_t* info, void* void_context) {
+ if (signal != SIGSYS)
+ return;
+ if (info->si_code != SYS_SECCOMP)
+ return;
+ if (!void_context)
+ return;
+ ucontext_t* context = reinterpret_cast<ucontext_t*>(void_context);
+ unsigned int syscall = context->uc_mcontext.gregs[REG_RAX];
+ if (syscall >= 1024)
+ syscall = 0;
+ // 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';
+ _exit(1);
+}
+
+static void InstallSIGSYSHandler() {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_SIGINFO;
+ sa.sa_sigaction = SIGSYS_Handler;
+ int ret = sigaction(SIGSYS, &sa, NULL);
+ PLOG_IF(FATAL, ret != 0) << "Failed to install SIGSYS handler.";
+}
+
+static void EmitLoad(int offset, std::vector<struct sock_filter>* program) {
+ struct sock_filter filter;
+ filter.code = BPF_LD+BPF_W+BPF_ABS;
+ filter.jt = 0;
+ filter.jf = 0;
+ filter.k = offset;
+ program->push_back(filter);
+}
+
+static void EmitJEQJT1(int value, std::vector<struct sock_filter>* program) {
+ struct sock_filter filter;
+ filter.code = BPF_JMP+BPF_JEQ+BPF_K;
+ filter.jt = 1;
+ filter.jf = 0;
+ filter.k = value;
+ program->push_back(filter);
+}
+
+static void EmitJEQJF1(int value, std::vector<struct sock_filter>* program) {
+ struct sock_filter filter;
+ filter.code = BPF_JMP+BPF_JEQ+BPF_K;
+ filter.jt = 0;
+ filter.jf = 1;
+ filter.k = value;
+ program->push_back(filter);
+}
+
+static void EmitRet(int value, std::vector<struct sock_filter>* program) {
+ struct sock_filter filter;
+ filter.code = BPF_RET+BPF_K;
+ filter.jt = 0;
+ filter.jf = 0;
+ filter.k = value;
+ program->push_back(filter);
+}
+
+static void EmitPreamble(std::vector<struct sock_filter>* program) {
+ // First, check correct syscall arch.
+ // "4" is magic offset for the arch number.
+ EmitLoad(4, program);
+ EmitJEQJT1(AUDIT_ARCH_X86_64, program);
+ EmitRet(SECCOMP_RET_KILL, program);
+
+ // Load the syscall number.
+ // "0" is magic offset for the syscall number.
+ EmitLoad(0, program);
+}
+
+static void EmitAllowSyscall(int nr, std::vector<struct sock_filter>* program) {
+ EmitJEQJF1(nr, program);
+ EmitRet(SECCOMP_RET_ALLOW, program);
+}
+
+static void EmitFailSyscall(int nr, int err,
+ std::vector<struct sock_filter>* program) {
+ EmitJEQJF1(nr, program);
+ EmitRet(SECCOMP_RET_ERRNO | err, program);
+}
+
+static void EmitTrap(std::vector<struct sock_filter>* program) {
+ EmitRet(SECCOMP_RET_TRAP, program);
+}
+
+static void ApplyGPUPolicy(std::vector<struct sock_filter>* program) {
+ // "Hot" syscalls go first.
+ EmitAllowSyscall(__NR_read, program);
+ EmitAllowSyscall(__NR_ioctl, program);
+ EmitAllowSyscall(__NR_poll, program);
+ EmitAllowSyscall(__NR_epoll_wait, program);
+ EmitAllowSyscall(__NR_recvfrom, program);
+ EmitAllowSyscall(__NR_write, program);
+ EmitAllowSyscall(__NR_writev, program);
+ EmitAllowSyscall(__NR_gettid, program);
+
+ // Less hot syscalls.
+ EmitAllowSyscall(__NR_futex, program);
+ EmitAllowSyscall(__NR_madvise, program);
+ EmitAllowSyscall(__NR_sendmsg, program);
+ EmitAllowSyscall(__NR_recvmsg, program);
+ EmitAllowSyscall(__NR_eventfd2, program);
+ EmitAllowSyscall(__NR_pipe, program);
+ EmitAllowSyscall(__NR_mmap, program);
+ EmitAllowSyscall(__NR_mprotect, program);
+ EmitAllowSyscall(__NR_clone, program);
+ EmitAllowSyscall(__NR_set_robust_list, program);
+ EmitAllowSyscall(__NR_getuid, program);
+ EmitAllowSyscall(__NR_geteuid, program);
+ EmitAllowSyscall(__NR_getgid, program);
+ EmitAllowSyscall(__NR_getegid, program);
+ EmitAllowSyscall(__NR_epoll_create, program);
+ EmitAllowSyscall(__NR_fcntl, program);
+ EmitAllowSyscall(__NR_socketpair, program);
+ EmitAllowSyscall(__NR_epoll_ctl, program);
+ EmitAllowSyscall(__NR_prctl, program);
+ EmitAllowSyscall(__NR_fstat, program);
+ EmitAllowSyscall(__NR_close, program);
+ EmitAllowSyscall(__NR_restart_syscall, program);
+ EmitAllowSyscall(__NR_rt_sigreturn, program);
+ EmitAllowSyscall(__NR_brk, program);
+ EmitAllowSyscall(__NR_rt_sigprocmask, program);
+ EmitAllowSyscall(__NR_munmap, program);
+ EmitAllowSyscall(__NR_dup, program);
+ EmitAllowSyscall(__NR_mlock, program);
+ EmitAllowSyscall(__NR_munlock, program);
+ EmitAllowSyscall(__NR_exit, program);
+ EmitAllowSyscall(__NR_exit_group, program);
+
+ EmitFailSyscall(__NR_open, ENOENT, program);
+ EmitFailSyscall(__NR_access, ENOENT, program);
+}
+
+static bool CanUseSeccompFilters() {
+ int ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, 0, 0, 0);
+ if (ret != 0 && errno == EFAULT)
+ return true;
+ return false;
+}
+
+static void InstallFilter(const std::vector<struct sock_filter>& program) {
+ int ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ PLOG_IF(FATAL, ret != 0) << "prctl(PR_SET_NO_NEW_PRIVS) failed";
+
+ struct sock_fprog fprog;
+ fprog.len = program.size();
+ fprog.filter = const_cast<struct sock_filter*>(&program[0]);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &fprog, 0, 0);
+ PLOG_IF(FATAL, ret != 0) << "Failed to install filter.";
+}
+
+} // anonymous namespace
+
+namespace content {
+
+void InitializeSandbox() {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ if (command_line.HasSwitch(switches::kNoSandbox))
+ return;
+
+ std::string process_type =
+ command_line.GetSwitchValueASCII(switches::kProcessType);
+ if (process_type == switches::kGpuProcess &&
+ command_line.HasSwitch(switches::kDisableGpuSandbox))
+ return;
+
+ if (!CanUseSeccompFilters())
+ return;
+
+ CheckSingleThreaded();
+
+ std::vector<struct sock_filter> program;
+ EmitPreamble(&program);
+
+ if (process_type == switches::kGpuProcess) {
+ ApplyGPUPolicy(&program);
+ } else {
+ NOTREACHED();
+ }
+
+ EmitTrap(&program);
+
+ InstallSIGSYSHandler();
+ InstallFilter(program);
+}
+
+} // namespace content
+
+#else
+
+namespace content {
+
+void InitializeSandbox() {
+}
+
+} // namespace content
+
+#endif
+
diff --git a/content/content_common.gypi b/content/content_common.gypi
index 5c5c04b..8aee0df 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -285,6 +285,7 @@
'common/sandbox_init_mac.cc',
'common/sandbox_init_mac.h',
'common/sandbox_init_win.cc',
+ 'common/sandbox_init_linux.cc',
'common/sandbox_mac.h',
'common/sandbox_mac.mm',
'common/sandbox_methods_linux.h',
diff --git a/content/gpu/gpu_info_collector.cc b/content/gpu/gpu_info_collector.cc
index 78e7888..ecdb189 100644
--- a/content/gpu/gpu_info_collector.cc
+++ b/content/gpu/gpu_info_collector.cc
@@ -101,6 +101,10 @@ bool CollectGraphicsInfoGL(content::GPUInfo* gpu_info) {
bool validVideoCardInfo = CollectVideoCardInfo(gpu_info);
bool validDriverInfo = CollectDriverInfoGL(gpu_info);
+ // TODO(kbr): remove once the destruction of a current context automatically
+ // clears the current context.
+ context->ReleaseCurrent(surface.get());
+
return (validGLVersionInfo && validVideoCardInfo && validDriverInfo);
}
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
index 2562cb3..dab200f 100644
--- a/content/gpu/gpu_main.cc
+++ b/content/gpu/gpu_main.cc
@@ -10,6 +10,7 @@
#include "base/environment.h"
#include "base/message_loop.h"
+#include "base/rand_util.h"
#include "base/stringprintf.h"
#include "base/threading/platform_thread.h"
#include "base/win/scoped_com_initializer.h"
@@ -37,6 +38,10 @@
#include "ui/gfx/gtk_util.h"
#endif
+#if defined(OS_LINUX)
+#include "content/public/common/sandbox_init.h"
+#endif
+
// Main function for starting the Gpu process.
int GpuMain(const content::MainFunctionParams& parameters) {
base::Time start_time = base::Time::Now();
@@ -100,16 +105,20 @@ int GpuMain(const content::MainFunctionParams& parameters) {
dead_on_arrival = true;
}
+ // Warm up the random subsystem, which needs to done pre-sandbox on all
+ // platforms.
+ (void) base::RandUint64();
+
+#if defined(OS_LINUX)
+ content::InitializeSandbox();
+#endif
+
base::win::ScopedCOMInitializer com_initializer;
#if defined(OS_WIN)
// Preload this DLL because the sandbox prevents it from loading.
LoadLibrary(L"setupapi.dll");
- // Cause advapi32 to load before the sandbox is turned on.
- unsigned int dummy_rand;
- rand_s(&dummy_rand);
-
sandbox::TargetServices* target_services =
parameters.sandbox_info->target_services;
// Initialize H/W video decoding stuff which fails in the sandbox.
diff --git a/content/public/common/sandbox_init.h b/content/public/common/sandbox_init.h
index 88c6d839..26b7d38 100644
--- a/content/public/common/sandbox_init.h
+++ b/content/public/common/sandbox_init.h
@@ -46,6 +46,8 @@ CONTENT_EXPORT bool InitializeSandbox(
// taken and true is always returned.
CONTENT_EXPORT bool InitializeSandbox(int sandbox_type,
const FilePath& allowed_path);
+#elif defined(OS_LINUX)
+CONTENT_EXPORT void InitializeSandbox();
#endif
} // namespace content