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

Side by Side Diff: sandbox/linux/seccomp-bpf/sandbox_bpf.cc

Issue 759473002: Linux sandbox: change seccomp detection and initialization. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@refactor_startsandbox
Patch Set: Rebase Created 6 years 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" 5 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
6 6
7 // Some headers on Android are missing cdefs: crbug.com/172337. 7 // Some headers on Android are missing cdefs: crbug.com/172337.
8 // (We can't use OS_ANDROID here since build_config.h is not included). 8 // (We can't use OS_ANDROID here since build_config.h is not included).
9 #if defined(ANDROID) 9 #if defined(ANDROID)
10 #include <sys/cdefs.h> 10 #include <sys/cdefs.h>
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
45 #include "sandbox/linux/services/thread_helpers.h" 45 #include "sandbox/linux/services/thread_helpers.h"
46 46
47 using sandbox::bpf_dsl::Allow; 47 using sandbox::bpf_dsl::Allow;
48 using sandbox::bpf_dsl::Error; 48 using sandbox::bpf_dsl::Error;
49 using sandbox::bpf_dsl::ResultExpr; 49 using sandbox::bpf_dsl::ResultExpr;
50 50
51 namespace sandbox { 51 namespace sandbox {
52 52
53 namespace { 53 namespace {
54 54
55 const int kExpectedExitCode = 100;
56
57 #if !defined(NDEBUG)
58 void WriteFailedStderrSetupMessage(int out_fd) {
59 const char* error_string = strerror(errno);
60 static const char msg[] =
61 "You have reproduced a puzzling issue.\n"
62 "Please, report to crbug.com/152530!\n"
63 "Failed to set up stderr: ";
64 if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg) - 1)) > 0 && error_string &&
65 HANDLE_EINTR(write(out_fd, error_string, strlen(error_string))) > 0 &&
66 HANDLE_EINTR(write(out_fd, "\n", 1))) {
67 }
68 }
69 #endif // !defined(NDEBUG)
70
71 // We define a really simple sandbox policy. It is just good enough for us
72 // to tell that the sandbox has actually been activated.
73 class ProbePolicy : public bpf_dsl::Policy {
74 public:
75 ProbePolicy() {}
76 virtual ~ProbePolicy() {}
77
78 virtual ResultExpr EvaluateSyscall(int sysnum) const override {
79 switch (sysnum) {
80 case __NR_getpid:
81 // Return EPERM so that we can check that the filter actually ran.
82 return Error(EPERM);
83 case __NR_exit_group:
84 // Allow exit() with a non-default return code.
85 return Allow();
86 default:
87 // Make everything else fail in an easily recognizable way.
88 return Error(EINVAL);
89 }
90 }
91
92 private:
93 DISALLOW_COPY_AND_ASSIGN(ProbePolicy);
94 };
95
96 void ProbeProcess(void) {
97 if (sys_getpid() < 0 && errno == EPERM) {
98 sys_exit_group(kExpectedExitCode);
99 }
100 }
101
102 class AllowAllPolicy : public bpf_dsl::Policy {
103 public:
104 AllowAllPolicy() {}
105 virtual ~AllowAllPolicy() {}
106
107 virtual ResultExpr EvaluateSyscall(int sysnum) const override {
108 DCHECK(SandboxBPF::IsValidSyscallNumber(sysnum));
109 return Allow();
110 }
111
112 private:
113 DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy);
114 };
115
116 void TryVsyscallProcess(void) {
117 time_t current_time;
118 // time() is implemented as a vsyscall. With an older glibc, with
119 // vsyscall=emulate and some versions of the seccomp BPF patch
120 // we may get SIGKILL-ed. Detect this!
121 if (time(&current_time) != static_cast<time_t>(-1)) {
122 sys_exit_group(kExpectedExitCode);
123 }
124 }
125
126 bool IsSingleThreaded(int proc_task_fd) { 55 bool IsSingleThreaded(int proc_task_fd) {
127 return ThreadHelpers::IsSingleThreaded(proc_task_fd); 56 return ThreadHelpers::IsSingleThreaded(proc_task_fd);
128 } 57 }
129 58
59 // Check if the kernel supports seccomp-filter (a.k.a. seccomp mode 2) via
60 // prctl().
61 bool KernelSupportsSeccompBPF() {
62 errno = 0;
63 int rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr);
hidehiko 2014/11/25 11:47:36 As for nullptr, although PNaCl toolchain uses clan
jln (very slow on Chromium) 2014/11/25 18:16:08 (1) is not an option since we use C++11 both in ba
64
65 if (-1 == rv && EFAULT == errno) {
66 return true;
67 }
68 return false;
69 }
70
71 // Check if the kernel supports seccomp-filter via the seccomp system call
72 // and the TSYNC feature to enable seccomp on all threads.
73 bool KernelSupportsSeccompTsync() {
74 const int rv =
75 sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, nullptr);
76
77 if (rv == -1 && errno == EFAULT) {
78 return true;
79 } else {
80 // TODO(jln): turn these into DCHECK after 417888 is considered fixed.
81 CHECK_EQ(-1, rv);
82 CHECK(ENOSYS == errno || EINVAL == errno);
83 return false;
84 }
85 }
86
130 } // namespace 87 } // namespace
131 88
132 SandboxBPF::SandboxBPF() 89 SandboxBPF::SandboxBPF()
133 : quiet_(false), proc_task_fd_(-1), sandbox_has_started_(false), policy_() { 90 : quiet_(false), proc_task_fd_(-1), sandbox_has_started_(false), policy_() {
134 } 91 }
135 92
136 SandboxBPF::~SandboxBPF() { 93 SandboxBPF::~SandboxBPF() {
137 if (proc_task_fd_ != -1) 94 if (proc_task_fd_ != -1)
138 IGNORE_EINTR(close(proc_task_fd_)); 95 IGNORE_EINTR(close(proc_task_fd_));
139 } 96 }
140 97
141 bool SandboxBPF::IsValidSyscallNumber(int sysnum) { 98 bool SandboxBPF::IsValidSyscallNumber(int sysnum) {
142 return SyscallSet::IsValid(sysnum); 99 return SyscallSet::IsValid(sysnum);
143 } 100 }
144 101
145 bool SandboxBPF::RunFunctionInPolicy(void (*code_in_sandbox)(), 102 // static
146 scoped_ptr<bpf_dsl::Policy> policy) { 103 int SandboxBPF::SupportsSeccompSandbox() {
147 // Block all signals before forking a child process. This prevents an 104 if (KernelSupportsSeccompTsync()) {
148 // attacker from manipulating our test by sending us an unexpected signal. 105 DCHECK(KernelSupportsSeccompBPF());
149 sigset_t old_mask, new_mask; 106 return SECCOMP_SINGLE_THREADED | SECCOMP_MULTI_THREADED;
150 if (sigfillset(&new_mask) || sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) {
151 SANDBOX_DIE("sigprocmask() failed");
152 }
153 int fds[2];
154 if (pipe2(fds, O_NONBLOCK | O_CLOEXEC)) {
155 SANDBOX_DIE("pipe() failed");
156 } 107 }
157 108
158 if (fds[0] <= 2 || fds[1] <= 2) { 109 if (KernelSupportsSeccompBPF()) {
159 SANDBOX_DIE("Process started without standard file descriptors"); 110 return SECCOMP_SINGLE_THREADED;
160 } 111 }
161 112 return SECCOMP_NONE;
162 // This code is using fork() and should only ever run single-threaded.
163 // Most of the code below is "async-signal-safe" and only minor changes
164 // would be needed to support threads.
165 DCHECK(IsSingleThreaded(proc_task_fd_));
166 pid_t pid = fork();
167 if (pid < 0) {
168 // Die if we cannot fork(). We would probably fail a little later
169 // anyway, as the machine is likely very close to running out of
170 // memory.
171 // But what we don't want to do is return "false", as a crafty
172 // attacker might cause fork() to fail at will and could trick us
173 // into running without a sandbox.
174 sigprocmask(SIG_SETMASK, &old_mask, NULL); // OK, if it fails
175 SANDBOX_DIE("fork() failed unexpectedly");
176 }
177
178 // In the child process
179 if (!pid) {
180 // Test a very simple sandbox policy to verify that we can
181 // successfully turn on sandboxing.
182 Die::EnableSimpleExit();
183
184 errno = 0;
185 if (IGNORE_EINTR(close(fds[0]))) {
186 // This call to close() has been failing in strange ways. See
187 // crbug.com/152530. So we only fail in debug mode now.
188 #if !defined(NDEBUG)
189 WriteFailedStderrSetupMessage(fds[1]);
190 SANDBOX_DIE(NULL);
191 #endif
192 }
193 if (HANDLE_EINTR(dup2(fds[1], 2)) != 2) {
194 // Stderr could very well be a file descriptor to .xsession-errors, or
195 // another file, which could be backed by a file system that could cause
196 // dup2 to fail while trying to close stderr. It's important that we do
197 // not fail on trying to close stderr.
198 // If dup2 fails here, we will continue normally, this means that our
199 // parent won't cause a fatal failure if something writes to stderr in
200 // this child.
201 #if !defined(NDEBUG)
202 // In DEBUG builds, we still want to get a report.
203 WriteFailedStderrSetupMessage(fds[1]);
204 SANDBOX_DIE(NULL);
205 #endif
206 }
207 if (IGNORE_EINTR(close(fds[1]))) {
208 // This call to close() has been failing in strange ways. See
209 // crbug.com/152530. So we only fail in debug mode now.
210 #if !defined(NDEBUG)
211 WriteFailedStderrSetupMessage(fds[1]);
212 SANDBOX_DIE(NULL);
213 #endif
214 }
215
216 SetSandboxPolicy(policy.release());
217 if (!StartSandbox(PROCESS_SINGLE_THREADED)) {
218 SANDBOX_DIE(NULL);
219 }
220
221 // Run our code in the sandbox.
222 code_in_sandbox();
223
224 // code_in_sandbox() is not supposed to return here.
225 SANDBOX_DIE(NULL);
226 }
227
228 // In the parent process.
229 if (IGNORE_EINTR(close(fds[1]))) {
230 SANDBOX_DIE("close() failed");
231 }
232 if (sigprocmask(SIG_SETMASK, &old_mask, NULL)) {
233 SANDBOX_DIE("sigprocmask() failed");
234 }
235 int status;
236 if (HANDLE_EINTR(waitpid(pid, &status, 0)) != pid) {
237 SANDBOX_DIE("waitpid() failed unexpectedly");
238 }
239 bool rc = WIFEXITED(status) && WEXITSTATUS(status) == kExpectedExitCode;
240
241 // If we fail to support sandboxing, there might be an additional
242 // error message. If so, this was an entirely unexpected and fatal
243 // failure. We should report the failure and somebody must fix
244 // things. This is probably a security-critical bug in the sandboxing
245 // code.
246 if (!rc) {
247 char buf[4096];
248 ssize_t len = HANDLE_EINTR(read(fds[0], buf, sizeof(buf) - 1));
249 if (len > 0) {
250 while (len > 1 && buf[len - 1] == '\n') {
251 --len;
252 }
253 buf[len] = '\000';
254 SANDBOX_DIE(buf);
255 }
256 }
257 if (IGNORE_EINTR(close(fds[0]))) {
258 SANDBOX_DIE("close() failed");
259 }
260
261 return rc;
262 }
263
264 bool SandboxBPF::KernelSupportSeccompBPF() {
265 return RunFunctionInPolicy(ProbeProcess,
266 scoped_ptr<bpf_dsl::Policy>(new ProbePolicy())) &&
267 RunFunctionInPolicy(TryVsyscallProcess,
268 scoped_ptr<bpf_dsl::Policy>(new AllowAllPolicy()));
269 }
270
271 // static
272 SandboxBPF::SandboxStatus SandboxBPF::SupportsSeccompSandbox() {
273 if (status_ != STATUS_UNKNOWN) {
274 return status_;
275 }
276
277 // If we have not previously checked for availability of the sandbox or if
278 // we otherwise don't believe to have a good cached value, we have to
279 // perform a thorough check now.
280
281 // We create our own private copy of a "Sandbox" object. This ensures that
282 // the object does not have any policies configured, that might interfere
283 // with the tests done by "KernelSupportSeccompBPF()".
284 SandboxBPF sandbox;
285
286 // By setting "quiet_ = true" we suppress messages for expected and benign
287 // failures (e.g. if the current kernel lacks support for BPF filters).
288 // TODO(jln): use kernel API to check for seccomp support now that things
289 // have stabilized.
290 sandbox.quiet_ = true;
291 status_ =
292 sandbox.KernelSupportSeccompBPF() ? STATUS_AVAILABLE : STATUS_UNSUPPORTED;
293
294 return status_;
295 }
296
297 // static
298 SandboxBPF::SandboxStatus
299 SandboxBPF::SupportsSeccompThreadFilterSynchronization() {
300 // Applying NO_NEW_PRIVS, a BPF filter, and synchronizing the filter across
301 // the thread group are all handled atomically by this syscall.
302 const int rv = syscall(
303 __NR_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, NULL);
304
305 if (rv == -1 && errno == EFAULT) {
306 return STATUS_AVAILABLE;
307 } else {
308 // TODO(jln): turn these into DCHECK after 417888 is considered fixed.
309 CHECK_EQ(-1, rv);
310 CHECK(ENOSYS == errno || EINVAL == errno);
311 return STATUS_UNSUPPORTED;
312 }
313 } 113 }
314 114
315 void SandboxBPF::set_proc_task_fd(int proc_task_fd) { 115 void SandboxBPF::set_proc_task_fd(int proc_task_fd) {
316 proc_task_fd_ = proc_task_fd; 116 proc_task_fd_ = proc_task_fd;
317 } 117 }
318 118
319 bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) { 119 bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level) {
320 CHECK(thread_state == PROCESS_SINGLE_THREADED || 120 CHECK(seccomp_level == SECCOMP_SINGLE_THREADED ||
321 thread_state == PROCESS_MULTI_THREADED); 121 seccomp_level == SECCOMP_MULTI_THREADED);
322 122
323 if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) { 123 if (sandbox_has_started_) {
324 SANDBOX_DIE(
325 "Trying to start sandbox, even though it is known to be "
326 "unavailable");
327 return false;
328 } else if (sandbox_has_started_) {
329 SANDBOX_DIE( 124 SANDBOX_DIE(
330 "Cannot repeatedly start sandbox. Create a separate Sandbox " 125 "Cannot repeatedly start sandbox. Create a separate Sandbox "
331 "object instead."); 126 "object instead.");
332 return false; 127 return false;
333 } 128 }
334 129
335 bool supports_tsync = 130 const bool supports_tsync = KernelSupportsSeccompTsync();
336 SupportsSeccompThreadFilterSynchronization() == STATUS_AVAILABLE;
337 131
338 if (thread_state == PROCESS_SINGLE_THREADED) { 132 if (seccomp_level == SECCOMP_SINGLE_THREADED) {
339 if (!IsSingleThreaded(proc_task_fd_)) { 133 if (!IsSingleThreaded(proc_task_fd_)) {
340 SANDBOX_DIE("Cannot start sandbox; process is already multi-threaded"); 134 SANDBOX_DIE("Cannot start sandbox; process is already multi-threaded");
341 return false; 135 return false;
342 } 136 }
343 } else if (thread_state == PROCESS_MULTI_THREADED) { 137 } else if (seccomp_level == SECCOMP_MULTI_THREADED) {
344 if (IsSingleThreaded(proc_task_fd_)) { 138 if (IsSingleThreaded(proc_task_fd_)) {
345 SANDBOX_DIE("Cannot start sandbox; " 139 SANDBOX_DIE("Cannot start sandbox; "
346 "process may be single-threaded when reported as not"); 140 "process may be single-threaded when reported as not");
347 return false; 141 return false;
348 } 142 }
349 if (!supports_tsync) { 143 if (!supports_tsync) {
350 SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing " 144 SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing "
351 "filters for a threadgroup"); 145 "filters for a threadgroup");
352 return false; 146 return false;
353 } 147 }
354 } 148 }
355 149
356 // We no longer need access to any files in /proc. We want to do this 150 // We no longer need access to any files in /proc. We want to do this
357 // before installing the filters, just in case that our policy denies 151 // before installing the filters, just in case that our policy denies
358 // close(). 152 // close().
359 if (proc_task_fd_ >= 0) { 153 if (proc_task_fd_ >= 0) {
360 if (IGNORE_EINTR(close(proc_task_fd_))) { 154 if (IGNORE_EINTR(close(proc_task_fd_))) {
361 SANDBOX_DIE("Failed to close file descriptor for /proc"); 155 SANDBOX_DIE("Failed to close file descriptor for /proc");
362 return false; 156 return false;
363 } 157 }
364 proc_task_fd_ = -1; 158 proc_task_fd_ = -1;
365 } 159 }
366 160
367 // Install the filters. 161 // Install the filters.
368 InstallFilter(supports_tsync || thread_state == PROCESS_MULTI_THREADED); 162 InstallFilter(supports_tsync || seccomp_level == SECCOMP_MULTI_THREADED);
369
370 // We are now inside the sandbox.
371 status_ = STATUS_ENABLED;
372 163
373 return true; 164 return true;
374 } 165 }
375 166
376 // Don't take a scoped_ptr here, polymorphism make their use awkward. 167 // Don't take a scoped_ptr here, polymorphism make their use awkward.
377 void SandboxBPF::SetSandboxPolicy(bpf_dsl::Policy* policy) { 168 void SandboxBPF::SetSandboxPolicy(bpf_dsl::Policy* policy) {
378 DCHECK(!policy_); 169 DCHECK(!policy_);
379 if (sandbox_has_started_) { 170 if (sandbox_has_started_) {
380 SANDBOX_DIE("Cannot change policy after sandbox has started"); 171 SANDBOX_DIE("Cannot change policy after sandbox has started");
381 } 172 }
(...skipping 26 matching lines...) Expand all
408 policy_.reset(); 199 policy_.reset();
409 200
410 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { 201 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
411 SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to enable no-new-privs"); 202 SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to enable no-new-privs");
412 } 203 }
413 204
414 // Install BPF filter program. If the thread state indicates multi-threading 205 // Install BPF filter program. If the thread state indicates multi-threading
415 // support, then the kernel hass the seccomp system call. Otherwise, fall 206 // support, then the kernel hass the seccomp system call. Otherwise, fall
416 // back on prctl, which requires the process to be single-threaded. 207 // back on prctl, which requires the process to be single-threaded.
417 if (must_sync_threads) { 208 if (must_sync_threads) {
418 int rv = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 209 int rv =
419 SECCOMP_FILTER_FLAG_TSYNC, reinterpret_cast<const char*>(&prog)); 210 sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &prog);
420 if (rv) { 211 if (rv) {
421 SANDBOX_DIE(quiet_ ? NULL : 212 SANDBOX_DIE(quiet_ ? NULL :
422 "Kernel refuses to turn on and synchronize threads for BPF filters"); 213 "Kernel refuses to turn on and synchronize threads for BPF filters");
423 } 214 }
424 } else { 215 } else {
425 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { 216 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
426 SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to turn on BPF filters"); 217 SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to turn on BPF filters");
427 } 218 }
428 } 219 }
429 220
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
464 intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) { 255 intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) {
465 return Syscall::Call(args.nr, 256 return Syscall::Call(args.nr,
466 static_cast<intptr_t>(args.args[0]), 257 static_cast<intptr_t>(args.args[0]),
467 static_cast<intptr_t>(args.args[1]), 258 static_cast<intptr_t>(args.args[1]),
468 static_cast<intptr_t>(args.args[2]), 259 static_cast<intptr_t>(args.args[2]),
469 static_cast<intptr_t>(args.args[3]), 260 static_cast<intptr_t>(args.args[3]),
470 static_cast<intptr_t>(args.args[4]), 261 static_cast<intptr_t>(args.args[4]),
471 static_cast<intptr_t>(args.args[5])); 262 static_cast<intptr_t>(args.args[5]));
472 } 263 }
473 264
474 SandboxBPF::SandboxStatus SandboxBPF::status_ = STATUS_UNKNOWN;
475
476 } // namespace sandbox 265 } // namespace sandbox
OLDNEW
« no previous file with comments | « sandbox/linux/seccomp-bpf/sandbox_bpf.h ('k') | sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698