Index: content/common/sandbox_init_linux.cc |
=================================================================== |
--- content/common/sandbox_init_linux.cc (revision 0) |
+++ content/common/sandbox_init_linux.cc (revision 0) |
@@ -0,0 +1,259 @@ |
+// 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/filter.h> |
+#include <signal.h> |
+#include <stdio.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 |
+ |
+namespace { |
+ |
+static void CheckSingleThreaded() { |
jln (very slow on Chromium)
2012/04/12 17:54:14
This is racy if potentially created threads can cr
|
+ char proc_name[64]; |
+ snprintf(proc_name, sizeof(proc_name), "/proc/%d/task", getpid()); |
+ FilePath path(proc_name); |
+ int num_threads = file_util::CountFilesCreatedAfter( |
+ path, base::Time::UnixEpoch()); |
+ 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]; |
Will Drewry
2012/04/12 01:08:56
I guess this is easier than wrangling asm/siginfo?
Chris Evans
2012/04/12 18:42:49
Yeah, and it's also the way the Will / Kees exampl
Kees Cook
2012/04/12 18:56:39
Will found a way to include the kernel's siginfo.h
|
+ 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 EmitPreamble(std::vector<struct sock_filter>* program) { |
+ struct sock_filter filter; |
+ // First, check syscall arch. |
+ filter.code = BPF_LD+BPF_W+BPF_ABS; |
+ filter.jt = 0; |
+ filter.jf = 0; |
+ // Offset 4 for syscall arch. |
+ filter.k = 4; |
+ program->push_back(filter); |
+ filter.code = BPF_JMP+BPF_JEQ+BPF_K; |
+ filter.jt = 1; |
+ filter.jf = 0; |
+ // AUDIT_ARCH_X86_64 |
+ filter.k = 0xc000003e; |
+ program->push_back(filter); |
+ filter.code = BPF_RET+BPF_K; |
+ filter.jt = 0; |
+ filter.jf = 0; |
+ // SECCOMP_RET_KILL |
+ filter.k = 0; |
Kees Cook
2012/04/12 18:56:39
I would use the actual SECCOMP_* defines here and
|
+ program->push_back(filter); |
+ filter.code = BPF_LD+BPF_W+BPF_ABS; |
+ filter.jt = 0; |
+ filter.jf = 0; |
+ // Offset 0 for syscall number. |
+ filter.k = 0; |
+ program->push_back(filter); |
+} |
+ |
+static void EmitAllowSyscall(int nr, 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 = nr; |
+ program->push_back(filter); |
+ filter.code = BPF_RET+BPF_K; |
+ filter.jt = 0; |
+ filter.jf = 0; |
+ // SECCOMP_RET_ALLOW |
+ filter.k = 0x7fff0000; |
+ program->push_back(filter); |
+} |
+ |
+static void EmitFailSyscall(int nr, int err, |
+ 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 = nr; |
+ program->push_back(filter); |
+ filter.code = BPF_RET+BPF_K; |
+ filter.jt = 0; |
+ filter.jf = 0; |
+ // SECCOMP_RET_ERRNO |
+ filter.k = 0x00050000 | err; |
+ program->push_back(filter); |
+} |
+ |
+static void EmitKill(std::vector<struct sock_filter>* program) { |
Will Drewry
2012/04/12 01:08:56
nit: Any reason not to call it EmitLoggedKill? Th
Chris Evans
2012/04/12 18:42:49
Ah nice catch, I did previously used SECCOMP_RET_K
|
+ struct sock_filter filter; |
+ filter.code = BPF_RET+BPF_K; |
+ filter.jt = 0; |
+ filter.jf = 0; |
+ // SECCOMP_RET_TRAP |
+ filter.k = 0x00030000; |
+ program->push_back(filter); |
+} |
+ |
+static void ApplyGPUPolicy(std::vector<struct sock_filter>* program) { |
Will Drewry
2012/04/12 01:08:56
Is the plan to tighten this up further later?
Markus (顧孟勤)
2012/04/12 17:51:24
My ultimate plan for efficient syscall filtering i
Chris Evans
2012/04/12 18:42:49
Definitely. Once we've flushed out all the corner-
|
+ // "Hot" syscalls go first. |
jln (very slow on Chromium)
2012/04/12 17:54:14
Looks like a very good start with a positive secur
|
+ 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_NO_NEW_PRIVS, 1, 0, 0, 0); |
+ if (ret != 0) { |
+ if (errno == EINVAL) { |
+ // This is expected: not many kernels support this yet. |
+ return false; |
+ } |
+ PLOG(FATAL) << "prctl(PR_SET_NO_NEW_PRIVS) failed"; |
+ } |
Will Drewry
2012/04/12 01:08:56
An authoritative check would be:
ret = prctl(PR_
Markus (顧孟勤)
2012/04/12 17:51:24
If you don't mind the minimal cost of doing an ext
Chris Evans
2012/04/12 18:42:49
Thanks! Much cleaner. I moved PR_SET_NEW_PRIVS to
Kees Cook
2012/04/12 18:56:39
I wonder if the filter should test for failure ins
|
+ return true; |
+} |
+ |
+static void InstallFilter(const std::vector<struct sock_filter>& program) { |
+ struct sock_fprog fprog; |
+ fprog.len = program.size(); |
+ fprog.filter = const_cast<struct sock_filter*>(&program[0]); |
+ |
+ int ret = prctl(PR_SET_SECCOMP, 2, &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; |
+ |
+ CheckSingleThreaded(); |
+ |
+ if (!CanUseSeccompFilters()) |
+ return; |
+ |
+ std::vector<struct sock_filter> program; |
+ EmitPreamble(&program); |
+ |
+ if (process_type == switches::kGpuProcess) { |
+ ApplyGPUPolicy(&program); |
+ } else { |
+ NOTREACHED(); |
+ } |
+ |
+ EmitKill(&program); |
+ |
+ InstallSIGSYSHandler(); |
+ InstallFilter(program); |
+} |
+ |
+} // namespace content |
+ |
+#else |
+ |
+namespace content { |
+ |
+void InitializeSandbox() { |
+} |
+ |
+} // namespace content |
+ |
+#endif |
+ |