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

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: 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 res = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr);
64
65 if (-1 == res && 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 // Applying NO_NEW_PRIVS, a BPF filter, and synchronizing the filter across
75 // the thread group are all handled atomically by this syscall.
76 const int rv =
77 sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, NULL);
78
79 if (rv == -1 && errno == EFAULT) {
80 return true;
81 } else {
82 // TODO(jln): turn these into DCHECK after 417888 is considered fixed.
83 CHECK_EQ(-1, rv);
84 CHECK(ENOSYS == errno || EINVAL == errno);
85 return false;
86 }
87 }
88
130 } // namespace 89 } // namespace
131 90
132 SandboxBPF::SandboxBPF() 91 SandboxBPF::SandboxBPF()
133 : quiet_(false), proc_task_fd_(-1), sandbox_has_started_(false), policy_() { 92 : quiet_(false), proc_task_fd_(-1), sandbox_has_started_(false), policy_() {
134 } 93 }
135 94
136 SandboxBPF::~SandboxBPF() { 95 SandboxBPF::~SandboxBPF() {
137 if (proc_task_fd_ != -1) 96 if (proc_task_fd_ != -1)
138 IGNORE_EINTR(close(proc_task_fd_)); 97 IGNORE_EINTR(close(proc_task_fd_));
139 } 98 }
140 99
141 bool SandboxBPF::IsValidSyscallNumber(int sysnum) { 100 bool SandboxBPF::IsValidSyscallNumber(int sysnum) {
142 return SyscallSet::IsValid(sysnum); 101 return SyscallSet::IsValid(sysnum);
143 } 102 }
144 103
145 bool SandboxBPF::RunFunctionInPolicy(void (*code_in_sandbox)(), 104 // static
146 scoped_ptr<bpf_dsl::Policy> policy) { 105 int SandboxBPF::SupportsSeccompSandbox() {
147 // Block all signals before forking a child process. This prevents an 106 if (KernelSupportsSeccompTsync()) {
148 // attacker from manipulating our test by sending us an unexpected signal. 107 DCHECK(KernelSupportsSeccompBPF());
149 sigset_t old_mask, new_mask; 108 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 } 109 }
157 110
158 if (fds[0] <= 2 || fds[1] <= 2) { 111 if (KernelSupportsSeccompBPF()) {
159 SANDBOX_DIE("Process started without standard file descriptors"); 112 return SECCOMP_SINGLE_THREADED;
160 } 113 }
161 114 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 } 115 }
314 116
315 void SandboxBPF::set_proc_task_fd(int proc_task_fd) { 117 void SandboxBPF::set_proc_task_fd(int proc_task_fd) {
316 proc_task_fd_ = proc_task_fd; 118 proc_task_fd_ = proc_task_fd;
317 } 119 }
318 120
319 bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) { 121 bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level) {
320 CHECK(thread_state == PROCESS_SINGLE_THREADED || 122 CHECK(seccomp_level == SECCOMP_SINGLE_THREADED ||
321 thread_state == PROCESS_MULTI_THREADED); 123 seccomp_level == SECCOMP_MULTI_THREADED);
322 124
323 if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) { 125 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( 126 SANDBOX_DIE(
330 "Cannot repeatedly start sandbox. Create a separate Sandbox " 127 "Cannot repeatedly start sandbox. Create a separate Sandbox "
331 "object instead."); 128 "object instead.");
332 return false; 129 return false;
333 } 130 }
334 131
335 bool supports_tsync = 132 const bool supports_tsync = KernelSupportsSeccompTsync();
336 SupportsSeccompThreadFilterSynchronization() == STATUS_AVAILABLE;
337 133
338 if (thread_state == PROCESS_SINGLE_THREADED) { 134 if (seccomp_level == SECCOMP_SINGLE_THREADED) {
339 if (!IsSingleThreaded(proc_task_fd_)) { 135 if (!IsSingleThreaded(proc_task_fd_)) {
340 SANDBOX_DIE("Cannot start sandbox; process is already multi-threaded"); 136 SANDBOX_DIE("Cannot start sandbox; process is already multi-threaded");
341 return false; 137 return false;
342 } 138 }
343 } else if (thread_state == PROCESS_MULTI_THREADED) { 139 } else if (seccomp_level == SECCOMP_MULTI_THREADED) {
344 if (IsSingleThreaded(proc_task_fd_)) { 140 if (IsSingleThreaded(proc_task_fd_)) {
345 SANDBOX_DIE("Cannot start sandbox; " 141 SANDBOX_DIE("Cannot start sandbox; "
346 "process may be single-threaded when reported as not"); 142 "process may be single-threaded when reported as not");
347 return false; 143 return false;
348 } 144 }
349 if (!supports_tsync) { 145 if (!supports_tsync) {
350 SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing " 146 SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing "
351 "filters for a threadgroup"); 147 "filters for a threadgroup");
352 return false; 148 return false;
353 } 149 }
354 } 150 }
355 151
356 // We no longer need access to any files in /proc. We want to do this 152 // 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 153 // before installing the filters, just in case that our policy denies
358 // close(). 154 // close().
359 if (proc_task_fd_ >= 0) { 155 if (proc_task_fd_ >= 0) {
360 if (IGNORE_EINTR(close(proc_task_fd_))) { 156 if (IGNORE_EINTR(close(proc_task_fd_))) {
361 SANDBOX_DIE("Failed to close file descriptor for /proc"); 157 SANDBOX_DIE("Failed to close file descriptor for /proc");
362 return false; 158 return false;
363 } 159 }
364 proc_task_fd_ = -1; 160 proc_task_fd_ = -1;
365 } 161 }
366 162
367 // Install the filters. 163 // Install the filters.
368 InstallFilter(supports_tsync || thread_state == PROCESS_MULTI_THREADED); 164 InstallFilter(supports_tsync || seccomp_level == SECCOMP_MULTI_THREADED);
369
370 // We are now inside the sandbox.
371 status_ = STATUS_ENABLED;
372 165
373 return true; 166 return true;
374 } 167 }
375 168
376 // Don't take a scoped_ptr here, polymorphism make their use awkward. 169 // Don't take a scoped_ptr here, polymorphism make their use awkward.
377 void SandboxBPF::SetSandboxPolicy(bpf_dsl::Policy* policy) { 170 void SandboxBPF::SetSandboxPolicy(bpf_dsl::Policy* policy) {
378 DCHECK(!policy_); 171 DCHECK(!policy_);
379 if (sandbox_has_started_) { 172 if (sandbox_has_started_) {
380 SANDBOX_DIE("Cannot change policy after sandbox has started"); 173 SANDBOX_DIE("Cannot change policy after sandbox has started");
381 } 174 }
(...skipping 26 matching lines...) Expand all
408 policy_.reset(); 201 policy_.reset();
409 202
410 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { 203 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
411 SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to enable no-new-privs"); 204 SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to enable no-new-privs");
412 } 205 }
413 206
414 // Install BPF filter program. If the thread state indicates multi-threading 207 // Install BPF filter program. If the thread state indicates multi-threading
415 // support, then the kernel hass the seccomp system call. Otherwise, fall 208 // support, then the kernel hass the seccomp system call. Otherwise, fall
416 // back on prctl, which requires the process to be single-threaded. 209 // back on prctl, which requires the process to be single-threaded.
417 if (must_sync_threads) { 210 if (must_sync_threads) {
418 int rv = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 211 int rv =
419 SECCOMP_FILTER_FLAG_TSYNC, reinterpret_cast<const char*>(&prog)); 212 sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &prog);
420 if (rv) { 213 if (rv) {
421 SANDBOX_DIE(quiet_ ? NULL : 214 SANDBOX_DIE(quiet_ ? NULL :
422 "Kernel refuses to turn on and synchronize threads for BPF filters"); 215 "Kernel refuses to turn on and synchronize threads for BPF filters");
423 } 216 }
424 } else { 217 } else {
425 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { 218 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
426 SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to turn on BPF filters"); 219 SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to turn on BPF filters");
427 } 220 }
428 } 221 }
429 222
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
464 intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) { 257 intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) {
465 return Syscall::Call(args.nr, 258 return Syscall::Call(args.nr,
466 static_cast<intptr_t>(args.args[0]), 259 static_cast<intptr_t>(args.args[0]),
467 static_cast<intptr_t>(args.args[1]), 260 static_cast<intptr_t>(args.args[1]),
468 static_cast<intptr_t>(args.args[2]), 261 static_cast<intptr_t>(args.args[2]),
469 static_cast<intptr_t>(args.args[3]), 262 static_cast<intptr_t>(args.args[3]),
470 static_cast<intptr_t>(args.args[4]), 263 static_cast<intptr_t>(args.args[4]),
471 static_cast<intptr_t>(args.args[5])); 264 static_cast<intptr_t>(args.args[5]));
472 } 265 }
473 266
474 SandboxBPF::SandboxStatus SandboxBPF::status_ = STATUS_UNKNOWN;
475
476 } // namespace sandbox 267 } // namespace sandbox
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698