OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/public/common/sandbox_init.h" | |
6 | |
7 #if defined(OS_LINUX) && defined(__x86_64__) | |
8 | |
9 #include <asm/unistd.h> | |
10 #include <errno.h> | |
11 #include <linux/audit.h> | |
12 #include <linux/filter.h> | |
13 #include <signal.h> | |
14 #include <string.h> | |
15 #include <sys/prctl.h> | |
16 #include <ucontext.h> | |
17 #include <unistd.h> | |
18 | |
19 #include <vector> | |
20 | |
21 #include "base/command_line.h" | |
22 #include "base/file_util.h" | |
23 #include "base/logging.h" | |
24 #include "base/time.h" | |
25 #include "content/public/common/content_switches.h" | |
26 | |
27 #ifndef PR_SET_NO_NEW_PRIVS | |
28 #define PR_SET_NO_NEW_PRIVS 38 | |
29 #endif | |
30 | |
31 #ifndef SYS_SECCOMP | |
32 #define SYS_SECCOMP 1 | |
33 #endif | |
34 | |
35 #ifndef __NR_eventfd2 | |
36 #define __NR_eventfd2 290 | |
37 #endif | |
38 | |
39 // Constants from very new header files that we can't yet include. | |
40 #ifndef SECCOMP_MODE_FILTER | |
41 #define SECCOMP_MODE_FILTER 2 | |
42 #define SECCOMP_RET_KILL 0x00000000U | |
43 #define SECCOMP_RET_TRAP 0x00030000U | |
44 #define SECCOMP_RET_ERRNO 0x00050000U | |
45 #define SECCOMP_RET_ALLOW 0x7fff0000U | |
46 #endif | |
47 | |
48 | |
49 namespace { | |
50 | |
51 static void CheckSingleThreaded() { | |
52 int num_threads = file_util::CountFilesCreatedAfter( | |
53 FilePath("/proc/self/task"), base::Time::UnixEpoch()); | |
54 // Possibly racy, but it's ok because this is more of a debug check to catch | |
55 // new threaded situations arising during development. | |
56 CHECK_EQ(num_threads, 1); | |
57 } | |
58 | |
59 static void SIGSYS_Handler(int signal, siginfo_t* info, void* void_context) { | |
60 if (signal != SIGSYS) | |
61 return; | |
62 if (info->si_code != SYS_SECCOMP) | |
63 return; | |
64 if (!void_context) | |
65 return; | |
66 ucontext_t* context = reinterpret_cast<ucontext_t*>(void_context); | |
67 unsigned int syscall = context->uc_mcontext.gregs[REG_RAX]; | |
68 if (syscall >= 1024) | |
69 syscall = 0; | |
70 // Purposefully dereference the syscall as an address so it'll show up very | |
71 // clearly and easily in crash dumps. | |
72 volatile char* addr = reinterpret_cast<volatile char*>(syscall); | |
73 *addr = '\0'; | |
74 _exit(1); | |
75 } | |
76 | |
77 static void InstallSIGSYSHandler() { | |
78 struct sigaction sa; | |
79 memset(&sa, 0, sizeof(sa)); | |
80 sa.sa_flags = SA_SIGINFO; | |
81 sa.sa_sigaction = SIGSYS_Handler; | |
82 int ret = sigaction(SIGSYS, &sa, NULL); | |
83 PLOG_IF(FATAL, ret != 0) << "Failed to install SIGSYS handler."; | |
84 } | |
85 | |
86 static void EmitPreamble(std::vector<struct sock_filter>* program) { | |
87 struct sock_filter filter; | |
88 // First, check syscall arch. | |
89 filter.code = BPF_LD+BPF_W+BPF_ABS; | |
90 filter.jt = 0; | |
91 filter.jf = 0; | |
92 // Offset 4 for syscall arch. | |
93 filter.k = 4; | |
Markus (顧孟勤)
2012/04/12 22:22:33
Instead of hardcoding the offset, you might want t
| |
94 program->push_back(filter); | |
95 | |
96 filter.code = BPF_JMP+BPF_JEQ+BPF_K; | |
97 filter.jt = 1; | |
98 filter.jf = 0; | |
99 filter.k = AUDIT_ARCH_X86_64; | |
100 program->push_back(filter); | |
101 | |
102 filter.code = BPF_RET+BPF_K; | |
103 filter.jt = 0; | |
104 filter.jf = 0; | |
105 filter.k = SECCOMP_RET_KILL; | |
106 program->push_back(filter); | |
107 | |
108 filter.code = BPF_LD+BPF_W+BPF_ABS; | |
109 filter.jt = 0; | |
110 filter.jf = 0; | |
111 // Offset 0 for syscall number. | |
112 filter.k = 0; | |
Markus (顧孟勤)
2012/04/12 22:22:33
Same here
| |
113 program->push_back(filter); | |
114 } | |
115 | |
116 static void EmitAllowSyscall(int nr, std::vector<struct sock_filter>* program) { | |
117 struct sock_filter filter; | |
118 filter.code = BPF_JMP+BPF_JEQ+BPF_K; | |
119 filter.jt = 0; | |
120 filter.jf = 1; | |
121 filter.k = nr; | |
122 program->push_back(filter); | |
123 | |
124 filter.code = BPF_RET+BPF_K; | |
125 filter.jt = 0; | |
126 filter.jf = 0; | |
127 filter.k = SECCOMP_RET_ALLOW; | |
Markus (顧孟勤)
2012/04/12 22:22:33
This generates twice as many instructions as neces
| |
128 program->push_back(filter); | |
129 } | |
130 | |
131 static void EmitFailSyscall(int nr, int err, | |
132 std::vector<struct sock_filter>* program) { | |
Markus (顧孟勤)
2012/04/12 22:22:33
EmitAllowSyscall() is really the same as EmitFailS
| |
133 struct sock_filter filter; | |
134 filter.code = BPF_JMP+BPF_JEQ+BPF_K; | |
135 filter.jt = 0; | |
136 filter.jf = 1; | |
137 filter.k = nr; | |
138 program->push_back(filter); | |
139 | |
140 filter.code = BPF_RET+BPF_K; | |
141 filter.jt = 0; | |
142 filter.jf = 0; | |
143 filter.k = SECCOMP_RET_ERRNO | err; | |
144 program->push_back(filter); | |
145 } | |
146 | |
147 static void EmitTrap(std::vector<struct sock_filter>* program) { | |
148 struct sock_filter filter; | |
149 filter.code = BPF_RET+BPF_K; | |
150 filter.jt = 0; | |
151 filter.jf = 0; | |
152 filter.k = SECCOMP_RET_TRAP; | |
153 program->push_back(filter); | |
154 } | |
155 | |
156 static void ApplyGPUPolicy(std::vector<struct sock_filter>* program) { | |
157 // "Hot" syscalls go first. | |
158 EmitAllowSyscall(__NR_read, program); | |
159 EmitAllowSyscall(__NR_ioctl, program); | |
Markus (顧孟勤)
2012/04/12 22:22:33
That sound dangerous. There are way too many thing
| |
160 EmitAllowSyscall(__NR_poll, program); | |
161 EmitAllowSyscall(__NR_epoll_wait, program); | |
162 EmitAllowSyscall(__NR_recvfrom, program); | |
163 EmitAllowSyscall(__NR_write, program); | |
164 EmitAllowSyscall(__NR_writev, program); | |
165 EmitAllowSyscall(__NR_gettid, program); | |
166 | |
167 // Less hot syscalls. | |
168 EmitAllowSyscall(__NR_futex, program); | |
169 EmitAllowSyscall(__NR_madvise, program); | |
Markus (顧孟勤)
2012/04/12 22:22:33
The memory functions (madvise, mmap, munmap, mprot
| |
170 EmitAllowSyscall(__NR_sendmsg, program); | |
Markus (顧孟勤)
2012/04/12 22:22:33
sendmsg() is potentially dangerous. At the very le
| |
171 EmitAllowSyscall(__NR_recvmsg, program); | |
172 EmitAllowSyscall(__NR_eventfd2, program); | |
173 EmitAllowSyscall(__NR_pipe, program); | |
174 EmitAllowSyscall(__NR_mmap, program); | |
175 EmitAllowSyscall(__NR_mprotect, program); | |
176 EmitAllowSyscall(__NR_clone, program); | |
Markus (顧孟勤)
2012/04/12 22:22:33
clone() can have surprising semantics. I'd suggest
| |
177 EmitAllowSyscall(__NR_set_robust_list, program); | |
178 EmitAllowSyscall(__NR_getuid, program); | |
179 EmitAllowSyscall(__NR_geteuid, program); | |
180 EmitAllowSyscall(__NR_getgid, program); | |
181 EmitAllowSyscall(__NR_getegid, program); | |
182 EmitAllowSyscall(__NR_epoll_create, program); | |
183 EmitAllowSyscall(__NR_fcntl, program); | |
184 EmitAllowSyscall(__NR_socketpair, program); | |
185 EmitAllowSyscall(__NR_epoll_ctl, program); | |
186 EmitAllowSyscall(__NR_prctl, program); | |
Markus (顧孟勤)
2012/04/12 22:22:33
I would strongly encourage you to limit prctl() to
| |
187 EmitAllowSyscall(__NR_fstat, program); | |
188 EmitAllowSyscall(__NR_close, program); | |
189 EmitAllowSyscall(__NR_restart_syscall, program); | |
Markus (顧孟勤)
2012/04/12 22:22:33
Is this actually visible to seccomp/BPF? I thought
jln (very slow on Chromium)
2012/04/12 22:36:47
Yes, the kernel puts them on the user stack when i
| |
190 EmitAllowSyscall(__NR_rt_sigreturn, program); | |
191 EmitAllowSyscall(__NR_brk, program); | |
192 EmitAllowSyscall(__NR_rt_sigprocmask, program); | |
193 EmitAllowSyscall(__NR_munmap, program); | |
194 EmitAllowSyscall(__NR_dup, program); | |
195 EmitAllowSyscall(__NR_mlock, program); | |
196 EmitAllowSyscall(__NR_munlock, program); | |
197 EmitAllowSyscall(__NR_exit, program); | |
198 EmitAllowSyscall(__NR_exit_group, program); | |
199 | |
200 EmitFailSyscall(__NR_open, ENOENT, program); | |
201 EmitFailSyscall(__NR_access, ENOENT, program); | |
202 } | |
203 | |
204 static bool CanUseSeccompFilters() { | |
205 int ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, 0, 0, 0); | |
206 if (ret != 0 && errno == EFAULT) | |
207 return true; | |
208 return false; | |
209 } | |
210 | |
211 static void InstallFilter(const std::vector<struct sock_filter>& program) { | |
212 int ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); | |
213 PLOG_IF(FATAL, ret != 0) << "prctl(PR_SET_NO_NEW_PRIVS) failed"; | |
214 | |
215 struct sock_fprog fprog; | |
216 fprog.len = program.size(); | |
217 fprog.filter = const_cast<struct sock_filter*>(&program[0]); | |
218 | |
219 ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &fprog, 0, 0); | |
220 PLOG_IF(FATAL, ret != 0) << "Failed to install filter."; | |
221 } | |
222 | |
223 } // anonymous namespace | |
224 | |
225 namespace content { | |
226 | |
227 void InitializeSandbox() { | |
228 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
229 if (command_line.HasSwitch(switches::kNoSandbox)) | |
230 return; | |
231 | |
232 std::string process_type = | |
233 command_line.GetSwitchValueASCII(switches::kProcessType); | |
234 if (process_type == switches::kGpuProcess && | |
235 command_line.HasSwitch(switches::kDisableGpuSandbox)) | |
236 return; | |
237 | |
238 CheckSingleThreaded(); | |
239 | |
240 if (!CanUseSeccompFilters()) | |
241 return; | |
242 | |
243 std::vector<struct sock_filter> program; | |
244 EmitPreamble(&program); | |
245 | |
246 if (process_type == switches::kGpuProcess) { | |
247 ApplyGPUPolicy(&program); | |
248 } else { | |
249 NOTREACHED(); | |
250 } | |
251 | |
252 EmitTrap(&program); | |
253 | |
254 InstallSIGSYSHandler(); | |
255 InstallFilter(program); | |
256 } | |
257 | |
258 } // namespace content | |
259 | |
260 #else | |
261 | |
262 namespace content { | |
263 | |
264 void InitializeSandbox() { | |
265 } | |
266 | |
267 } // namespace content | |
268 | |
269 #endif | |
270 | |
OLD | NEW |