diff options
author | cevans@chromium.org <cevans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-13 21:08:51 +0000 |
---|---|---|
committer | cevans@chromium.org <cevans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-13 21:08:51 +0000 |
commit | 2436a6b19ebb77226b96b0e37623b9b0785e172c (patch) | |
tree | e2967a8ae4f47dfd6a4620142bd79c75905d0e6f /content | |
parent | 5592bb5f24ebebd858d0f789b92aa87c5e420f5f (diff) | |
download | chromium_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.cc | 263 | ||||
-rw-r--r-- | content/content_common.gypi | 1 | ||||
-rw-r--r-- | content/gpu/gpu_info_collector.cc | 4 | ||||
-rw-r--r-- | content/gpu/gpu_main.cc | 17 | ||||
-rw-r--r-- | content/public/common/sandbox_init.h | 2 |
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 |