OLD | NEW |
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 <time.h> | 5 #include <time.h> |
6 | 6 |
7 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" | 7 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" |
8 #include "sandbox/linux/seccomp-bpf/verifier.h" | 8 #include "sandbox/linux/seccomp-bpf/verifier.h" |
9 | 9 |
10 // The kernel gives us a sandbox, we turn it into a playground :-) | 10 // The kernel gives us a sandbox, we turn it into a playground :-) |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
53 } | 53 } |
54 | 54 |
55 bool Sandbox::RunFunctionInPolicy(void (*CodeInSandbox)(), | 55 bool Sandbox::RunFunctionInPolicy(void (*CodeInSandbox)(), |
56 EvaluateSyscall syscallEvaluator, | 56 EvaluateSyscall syscallEvaluator, |
57 int proc_fd) { | 57 int proc_fd) { |
58 // Block all signals before forking a child process. This prevents an | 58 // Block all signals before forking a child process. This prevents an |
59 // attacker from manipulating our test by sending us an unexpected signal. | 59 // attacker from manipulating our test by sending us an unexpected signal. |
60 sigset_t oldMask, newMask; | 60 sigset_t oldMask, newMask; |
61 if (sigfillset(&newMask) || | 61 if (sigfillset(&newMask) || |
62 sigprocmask(SIG_BLOCK, &newMask, &oldMask)) { | 62 sigprocmask(SIG_BLOCK, &newMask, &oldMask)) { |
63 die("sigprocmask() failed"); | 63 SANDBOX_DIE("sigprocmask() failed"); |
64 } | 64 } |
65 int fds[2]; | 65 int fds[2]; |
66 if (pipe2(fds, O_NONBLOCK|O_CLOEXEC)) { | 66 if (pipe2(fds, O_NONBLOCK|O_CLOEXEC)) { |
67 die("pipe() failed"); | 67 SANDBOX_DIE("pipe() failed"); |
68 } | 68 } |
69 | 69 |
70 pid_t pid = fork(); | 70 pid_t pid = fork(); |
71 if (pid < 0) { | 71 if (pid < 0) { |
72 // Die if we cannot fork(). We would probably fail a little later | 72 // Die if we cannot fork(). We would probably fail a little later |
73 // anyway, as the machine is likely very close to running out of | 73 // anyway, as the machine is likely very close to running out of |
74 // memory. | 74 // memory. |
75 // But what we don't want to do is return "false", as a crafty | 75 // But what we don't want to do is return "false", as a crafty |
76 // attacker might cause fork() to fail at will and could trick us | 76 // attacker might cause fork() to fail at will and could trick us |
77 // into running without a sandbox. | 77 // into running without a sandbox. |
78 sigprocmask(SIG_SETMASK, &oldMask, NULL); // OK, if it fails | 78 sigprocmask(SIG_SETMASK, &oldMask, NULL); // OK, if it fails |
79 die("fork() failed unexpectedly"); | 79 SANDBOX_DIE("fork() failed unexpectedly"); |
80 } | 80 } |
81 | 81 |
82 // In the child process | 82 // In the child process |
83 if (!pid) { | 83 if (!pid) { |
84 // Test a very simple sandbox policy to verify that we can | 84 // Test a very simple sandbox policy to verify that we can |
85 // successfully turn on sandboxing. | 85 // successfully turn on sandboxing. |
86 dryRun_ = true; | 86 Die::EnableSimpleExit(); |
87 if (HANDLE_EINTR(close(fds[0])) || | 87 if (HANDLE_EINTR(close(fds[0])) || |
88 dup2(fds[1], 2) != 2 || | 88 dup2(fds[1], 2) != 2 || |
89 HANDLE_EINTR(close(fds[1]))) { | 89 HANDLE_EINTR(close(fds[1]))) { |
90 static const char msg[] = "Failed to set up stderr\n"; | 90 static const char msg[] = "Failed to set up stderr\n"; |
91 if (HANDLE_EINTR(write(fds[1], msg, sizeof(msg)-1))) { } | 91 if (HANDLE_EINTR(write(fds[1], msg, sizeof(msg)-1))) { } |
92 } else { | 92 } else { |
93 evaluators_.clear(); | 93 evaluators_.clear(); |
94 setSandboxPolicy(syscallEvaluator, NULL); | 94 setSandboxPolicy(syscallEvaluator, NULL); |
95 setProcFd(proc_fd); | 95 setProcFd(proc_fd); |
96 startSandbox(); | 96 startSandbox(); |
97 // Run our code in the sandbox | 97 // Run our code in the sandbox |
98 CodeInSandbox(); | 98 CodeInSandbox(); |
99 } | 99 } |
100 die(NULL); | 100 SANDBOX_DIE(NULL); |
101 } | 101 } |
102 | 102 |
103 // In the parent process. | 103 // In the parent process. |
104 if (HANDLE_EINTR(close(fds[1]))) { | 104 if (HANDLE_EINTR(close(fds[1]))) { |
105 die("close() failed"); | 105 SANDBOX_DIE("close() failed"); |
106 } | 106 } |
107 if (sigprocmask(SIG_SETMASK, &oldMask, NULL)) { | 107 if (sigprocmask(SIG_SETMASK, &oldMask, NULL)) { |
108 die("sigprocmask() failed"); | 108 SANDBOX_DIE("sigprocmask() failed"); |
109 } | 109 } |
110 int status; | 110 int status; |
111 if (HANDLE_EINTR(waitpid(pid, &status, 0)) != pid) { | 111 if (HANDLE_EINTR(waitpid(pid, &status, 0)) != pid) { |
112 die("waitpid() failed unexpectedly"); | 112 SANDBOX_DIE("waitpid() failed unexpectedly"); |
113 } | 113 } |
114 bool rc = WIFEXITED(status) && WEXITSTATUS(status) == 100; | 114 bool rc = WIFEXITED(status) && WEXITSTATUS(status) == 100; |
115 | 115 |
116 // If we fail to support sandboxing, there might be an additional | 116 // If we fail to support sandboxing, there might be an additional |
117 // error message. If so, this was an entirely unexpected and fatal | 117 // error message. If so, this was an entirely unexpected and fatal |
118 // failure. We should report the failure and somebody most fix | 118 // failure. We should report the failure and somebody most fix |
119 // things. This is probably a security-critical bug in the sandboxing | 119 // things. This is probably a security-critical bug in the sandboxing |
120 // code. | 120 // code. |
121 if (!rc) { | 121 if (!rc) { |
122 char buf[4096]; | 122 char buf[4096]; |
123 ssize_t len = HANDLE_EINTR(read(fds[0], buf, sizeof(buf) - 1)); | 123 ssize_t len = HANDLE_EINTR(read(fds[0], buf, sizeof(buf) - 1)); |
124 if (len > 0) { | 124 if (len > 0) { |
125 while (len > 1 && buf[len-1] == '\n') { | 125 while (len > 1 && buf[len-1] == '\n') { |
126 --len; | 126 --len; |
127 } | 127 } |
128 buf[len] = '\000'; | 128 buf[len] = '\000'; |
129 die(buf); | 129 SANDBOX_DIE(buf); |
130 } | 130 } |
131 } | 131 } |
132 if (HANDLE_EINTR(close(fds[0]))) { | 132 if (HANDLE_EINTR(close(fds[0]))) { |
133 die("close() failed"); | 133 SANDBOX_DIE("close() failed"); |
134 } | 134 } |
135 | 135 |
136 return rc; | 136 return rc; |
137 | 137 |
138 } | 138 } |
139 | 139 |
140 bool Sandbox::kernelSupportSeccompBPF(int proc_fd) { | 140 bool Sandbox::kernelSupportSeccompBPF(int proc_fd) { |
141 return RunFunctionInPolicy(probeProcess, Sandbox::probeEvaluator, proc_fd) && | 141 return RunFunctionInPolicy(probeProcess, Sandbox::probeEvaluator, proc_fd) && |
142 RunFunctionInPolicy(tryVsyscallProcess, Sandbox::allowAllEvaluator, | 142 RunFunctionInPolicy(tryVsyscallProcess, Sandbox::allowAllEvaluator, |
143 proc_fd); | 143 proc_fd); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
186 status_ = STATUS_UNAVAILABLE; | 186 status_ = STATUS_UNAVAILABLE; |
187 } | 187 } |
188 } | 188 } |
189 return status_; | 189 return status_; |
190 } | 190 } |
191 | 191 |
192 void Sandbox::setProcFd(int proc_fd) { | 192 void Sandbox::setProcFd(int proc_fd) { |
193 proc_fd_ = proc_fd; | 193 proc_fd_ = proc_fd; |
194 } | 194 } |
195 | 195 |
196 void Sandbox::startSandbox() { | 196 void Sandbox::startSandboxInternal(bool quiet) { |
197 if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) { | 197 if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) { |
198 die("Trying to start sandbox, even though it is known to be unavailable"); | 198 SANDBOX_DIE("Trying to start sandbox, even though it is known to be " |
| 199 "unavailable"); |
199 } else if (status_ == STATUS_ENABLED) { | 200 } else if (status_ == STATUS_ENABLED) { |
200 die("Cannot start sandbox recursively. Use multiple calls to " | 201 SANDBOX_DIE("Cannot start sandbox recursively. Use multiple calls to " |
201 "setSandboxPolicy() to stack policies instead"); | 202 "setSandboxPolicy() to stack policies instead"); |
202 } | 203 } |
203 if (proc_fd_ < 0) { | 204 if (proc_fd_ < 0) { |
204 proc_fd_ = open("/proc", O_RDONLY|O_DIRECTORY); | 205 proc_fd_ = open("/proc", O_RDONLY|O_DIRECTORY); |
205 } | 206 } |
206 if (proc_fd_ < 0) { | 207 if (proc_fd_ < 0) { |
207 // For now, continue in degraded mode, if we can't access /proc. | 208 // For now, continue in degraded mode, if we can't access /proc. |
208 // In the future, we might want to tighten this requirement. | 209 // In the future, we might want to tighten this requirement. |
209 } | 210 } |
210 if (!isSingleThreaded(proc_fd_)) { | 211 if (!isSingleThreaded(proc_fd_)) { |
211 die("Cannot start sandbox, if process is already multi-threaded"); | 212 SANDBOX_DIE("Cannot start sandbox, if process is already multi-threaded"); |
212 } | 213 } |
213 | 214 |
214 // We no longer need access to any files in /proc. We want to do this | 215 // We no longer need access to any files in /proc. We want to do this |
215 // before installing the filters, just in case that our policy denies | 216 // before installing the filters, just in case that our policy denies |
216 // close(). | 217 // close(). |
217 if (proc_fd_ >= 0) { | 218 if (proc_fd_ >= 0) { |
218 if (HANDLE_EINTR(close(proc_fd_))) { | 219 if (HANDLE_EINTR(close(proc_fd_))) { |
219 die("Failed to close file descriptor for /proc"); | 220 SANDBOX_DIE("Failed to close file descriptor for /proc"); |
220 } | 221 } |
221 proc_fd_ = -1; | 222 proc_fd_ = -1; |
222 } | 223 } |
223 | 224 |
224 // Install the filters. | 225 // Install the filters. |
225 installFilter(); | 226 installFilter(quiet); |
226 | 227 |
227 // We are now inside the sandbox. | 228 // We are now inside the sandbox. |
228 status_ = STATUS_ENABLED; | 229 status_ = STATUS_ENABLED; |
229 } | 230 } |
230 | 231 |
231 bool Sandbox::isSingleThreaded(int proc_fd) { | 232 bool Sandbox::isSingleThreaded(int proc_fd) { |
232 if (proc_fd < 0) { | 233 if (proc_fd < 0) { |
233 // Cannot determine whether program is single-threaded. Hope for | 234 // Cannot determine whether program is single-threaded. Hope for |
234 // the best... | 235 // the best... |
235 return true; | 236 return true; |
(...skipping 19 matching lines...) Expand all Loading... |
255 code <= (SECCOMP_RET_ERRNO + 4095)); | 256 code <= (SECCOMP_RET_ERRNO + 4095)); |
256 } | 257 } |
257 | 258 |
258 void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator, | 259 void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator, |
259 EvaluateArguments) { | 260 EvaluateArguments) { |
260 // Do some sanity checks on the policy. This will warn users if they do | 261 // Do some sanity checks on the policy. This will warn users if they do |
261 // things that are likely unsafe and unintended. | 262 // things that are likely unsafe and unintended. |
262 // We also have similar checks later, when we actually compile the BPF | 263 // We also have similar checks later, when we actually compile the BPF |
263 // program. That catches problems with incorrectly stacked evaluators. | 264 // program. That catches problems with incorrectly stacked evaluators. |
264 if (!isDenied(syscallEvaluator(-1))) { | 265 if (!isDenied(syscallEvaluator(-1))) { |
265 die("Negative system calls should always be disallowed by policy"); | 266 SANDBOX_DIE("Negative system calls should always be disallowed by policy"); |
266 } | 267 } |
267 #ifndef NDEBUG | 268 #ifndef NDEBUG |
268 #if defined(__i386__) || defined(__x86_64__) | 269 #if defined(__i386__) || defined(__x86_64__) |
269 #if defined(__x86_64__) && defined(__ILP32__) | 270 #if defined(__x86_64__) && defined(__ILP32__) |
270 for (unsigned int sysnum = MIN_SYSCALL & ~0x40000000u; | 271 for (unsigned int sysnum = MIN_SYSCALL & ~0x40000000u; |
271 sysnum <= (MAX_SYSCALL & ~0x40000000u); | 272 sysnum <= (MAX_SYSCALL & ~0x40000000u); |
272 ++sysnum) { | 273 ++sysnum) { |
273 if (!isDenied(syscallEvaluator(sysnum))) { | 274 if (!isDenied(syscallEvaluator(sysnum))) { |
274 die("In x32 mode, you should not allow any non-x32 system calls"); | 275 SANDBOX_DIE("In x32 mode, you should not allow any non-x32 system calls"); |
275 } | 276 } |
276 } | 277 } |
277 #else | 278 #else |
278 for (unsigned int sysnum = MIN_SYSCALL | 0x40000000u; | 279 for (unsigned int sysnum = MIN_SYSCALL | 0x40000000u; |
279 sysnum <= (MAX_SYSCALL | 0x40000000u); | 280 sysnum <= (MAX_SYSCALL | 0x40000000u); |
280 ++sysnum) { | 281 ++sysnum) { |
281 if (!isDenied(syscallEvaluator(sysnum))) { | 282 if (!isDenied(syscallEvaluator(sysnum))) { |
282 die("x32 system calls should be explicitly disallowed"); | 283 SANDBOX_DIE("x32 system calls should be explicitly disallowed"); |
283 } | 284 } |
284 } | 285 } |
285 #endif | 286 #endif |
286 #endif | 287 #endif |
287 #endif | 288 #endif |
288 // Check interesting boundary values just outside of the valid system call | 289 // Check interesting boundary values just outside of the valid system call |
289 // range: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF, MIN_SYSCALL-1, MAX_SYSCALL+1. | 290 // range: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF, MIN_SYSCALL-1, MAX_SYSCALL+1. |
290 // They all should be denied. | 291 // They all should be denied. |
291 if (!isDenied(syscallEvaluator(std::numeric_limits<int>::max())) || | 292 if (!isDenied(syscallEvaluator(std::numeric_limits<int>::max())) || |
292 !isDenied(syscallEvaluator(std::numeric_limits<int>::min())) || | 293 !isDenied(syscallEvaluator(std::numeric_limits<int>::min())) || |
293 !isDenied(syscallEvaluator(-1)) || | 294 !isDenied(syscallEvaluator(-1)) || |
294 !isDenied(syscallEvaluator(static_cast<int>(MIN_SYSCALL) - 1)) || | 295 !isDenied(syscallEvaluator(static_cast<int>(MIN_SYSCALL) - 1)) || |
295 !isDenied(syscallEvaluator(static_cast<int>(MAX_SYSCALL) + 1))) { | 296 !isDenied(syscallEvaluator(static_cast<int>(MAX_SYSCALL) + 1))) { |
296 die("Even for default-allow policies, you must never allow system calls " | 297 SANDBOX_DIE("Even for default-allow policies, you must never allow system " |
297 "outside of the standard system call range"); | 298 "calls outside of the standard system call range"); |
298 } | 299 } |
299 return; | 300 return; |
300 } | 301 } |
301 | 302 |
302 void Sandbox::setSandboxPolicy(EvaluateSyscall syscallEvaluator, | 303 void Sandbox::setSandboxPolicy(EvaluateSyscall syscallEvaluator, |
303 EvaluateArguments argumentEvaluator) { | 304 EvaluateArguments argumentEvaluator) { |
304 if (status_ == STATUS_ENABLED) { | 305 if (status_ == STATUS_ENABLED) { |
305 die("Cannot change policy after sandbox has started"); | 306 SANDBOX_DIE("Cannot change policy after sandbox has started"); |
306 } | 307 } |
307 policySanityChecks(syscallEvaluator, argumentEvaluator); | 308 policySanityChecks(syscallEvaluator, argumentEvaluator); |
308 evaluators_.push_back(std::make_pair(syscallEvaluator, argumentEvaluator)); | 309 evaluators_.push_back(std::make_pair(syscallEvaluator, argumentEvaluator)); |
309 } | 310 } |
310 | 311 |
311 void Sandbox::installFilter() { | 312 void Sandbox::installFilter(bool quiet) { |
312 // Verify that the user pushed a policy. | 313 // Verify that the user pushed a policy. |
313 if (evaluators_.empty()) { | 314 if (evaluators_.empty()) { |
314 filter_failed: | 315 filter_failed: |
315 die("Failed to configure system call filters"); | 316 SANDBOX_DIE("Failed to configure system call filters"); |
316 } | 317 } |
317 | 318 |
318 // Set new SIGSYS handler | 319 // Set new SIGSYS handler |
319 struct sigaction sa; | 320 struct sigaction sa; |
320 memset(&sa, 0, sizeof(sa)); | 321 memset(&sa, 0, sizeof(sa)); |
321 sa.sa_sigaction = &sigSys; | 322 sa.sa_sigaction = &sigSys; |
322 sa.sa_flags = SA_SIGINFO; | 323 sa.sa_flags = SA_SIGINFO; |
323 if (sigaction(SIGSYS, &sa, NULL) < 0) { | 324 if (sigaction(SIGSYS, &sa, NULL) < 0) { |
324 goto filter_failed; | 325 goto filter_failed; |
325 } | 326 } |
326 | 327 |
327 // Unmask SIGSYS | 328 // Unmask SIGSYS |
328 sigset_t mask; | 329 sigset_t mask; |
329 if (sigemptyset(&mask) || | 330 if (sigemptyset(&mask) || |
330 sigaddset(&mask, SIGSYS) || | 331 sigaddset(&mask, SIGSYS) || |
331 sigprocmask(SIG_UNBLOCK, &mask, NULL)) { | 332 sigprocmask(SIG_UNBLOCK, &mask, NULL)) { |
332 goto filter_failed; | 333 goto filter_failed; |
333 } | 334 } |
334 | 335 |
335 // We can't handle stacked evaluators, yet. We'll get there eventually | 336 // We can't handle stacked evaluators, yet. We'll get there eventually |
336 // though. Hang tight. | 337 // though. Hang tight. |
337 if (evaluators_.size() != 1) { | 338 if (evaluators_.size() != 1) { |
338 die("Not implemented"); | 339 SANDBOX_DIE("Not implemented"); |
339 } | 340 } |
340 | 341 |
341 // Assemble the BPF filter program. | 342 // Assemble the BPF filter program. |
342 Program *program = new Program(); | 343 Program *program = new Program(); |
343 if (!program) { | 344 if (!program) { |
344 die("Out of memory"); | 345 SANDBOX_DIE("Out of memory"); |
345 } | 346 } |
346 | 347 |
347 // If the architecture doesn't match SECCOMP_ARCH, disallow the | 348 // If the architecture doesn't match SECCOMP_ARCH, disallow the |
348 // system call. | 349 // system call. |
349 program->push_back((struct sock_filter) | 350 program->push_back((struct sock_filter) |
350 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct arch_seccomp_data, arch))); | 351 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct arch_seccomp_data, arch))); |
351 program->push_back((struct sock_filter) | 352 program->push_back((struct sock_filter) |
352 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_ARCH, 1, 0)); | 353 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_ARCH, 1, 0)); |
353 | 354 |
354 program->push_back((struct sock_filter) | 355 program->push_back((struct sock_filter) |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
389 emitJumpStatements(program, &rets, ranges.begin(), ranges.end()); | 390 emitJumpStatements(program, &rets, ranges.begin(), ranges.end()); |
390 emitReturnStatements(program, rets); | 391 emitReturnStatements(program, rets); |
391 } | 392 } |
392 | 393 |
393 // Make sure compilation resulted in BPF program that executes | 394 // Make sure compilation resulted in BPF program that executes |
394 // correctly. Otherwise, there is an internal error in our BPF compiler. | 395 // correctly. Otherwise, there is an internal error in our BPF compiler. |
395 // There is really nothing the caller can do until the bug is fixed. | 396 // There is really nothing the caller can do until the bug is fixed. |
396 #ifndef NDEBUG | 397 #ifndef NDEBUG |
397 const char *err = NULL; | 398 const char *err = NULL; |
398 if (!Verifier::verifyBPF(*program, evaluators_, &err)) { | 399 if (!Verifier::verifyBPF(*program, evaluators_, &err)) { |
399 die(err); | 400 SANDBOX_DIE(err); |
400 } | 401 } |
401 #endif | 402 #endif |
402 | 403 |
403 // We want to be very careful in not imposing any requirements on the | 404 // We want to be very careful in not imposing any requirements on the |
404 // policies that are set with setSandboxPolicy(). This means, as soon as | 405 // policies that are set with setSandboxPolicy(). This means, as soon as |
405 // the sandbox is active, we shouldn't be relying on libraries that could | 406 // the sandbox is active, we shouldn't be relying on libraries that could |
406 // be making system calls. This, for example, means we should avoid | 407 // be making system calls. This, for example, means we should avoid |
407 // using the heap and we should avoid using STL functions. | 408 // using the heap and we should avoid using STL functions. |
408 // Temporarily copy the contents of the "program" vector into a | 409 // Temporarily copy the contents of the "program" vector into a |
409 // stack-allocated array; and then explicitly destroy that object. | 410 // stack-allocated array; and then explicitly destroy that object. |
410 // This makes sure we don't ex- or implicitly call new/delete after we | 411 // This makes sure we don't ex- or implicitly call new/delete after we |
411 // installed the BPF filter program in the kernel. Depending on the | 412 // installed the BPF filter program in the kernel. Depending on the |
412 // system memory allocator that is in effect, these operators can result | 413 // system memory allocator that is in effect, these operators can result |
413 // in system calls to things like munmap() or brk(). | 414 // in system calls to things like munmap() or brk(). |
414 struct sock_filter bpf[program->size()]; | 415 struct sock_filter bpf[program->size()]; |
415 const struct sock_fprog prog = { | 416 const struct sock_fprog prog = { |
416 static_cast<unsigned short>(program->size()), bpf }; | 417 static_cast<unsigned short>(program->size()), bpf }; |
417 memcpy(bpf, &(*program)[0], sizeof(bpf)); | 418 memcpy(bpf, &(*program)[0], sizeof(bpf)); |
418 delete program; | 419 delete program; |
419 | 420 |
420 // Release memory that is no longer needed | 421 // Release memory that is no longer needed |
421 evaluators_.clear(); | 422 evaluators_.clear(); |
422 | 423 |
423 // Install BPF filter program | 424 // Install BPF filter program |
424 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | 425 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { |
425 die(dryRun_ ? NULL : "Kernel refuses to enable no-new-privs"); | 426 SANDBOX_DIE(quiet |
| 427 ? NULL : "Kernel refuses to enable no-new-privs"); |
426 } else { | 428 } else { |
427 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { | 429 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { |
428 die(dryRun_ ? NULL : "Kernel refuses to turn on BPF filters"); | 430 SANDBOX_DIE(quiet |
| 431 ? NULL : "Kernel refuses to turn on BPF filters"); |
429 } | 432 } |
430 } | 433 } |
431 | 434 |
432 return; | 435 return; |
433 } | 436 } |
434 | 437 |
435 void Sandbox::findRanges(Ranges *ranges) { | 438 void Sandbox::findRanges(Ranges *ranges) { |
436 // Please note that "struct seccomp_data" defines system calls as a signed | 439 // Please note that "struct seccomp_data" defines system calls as a signed |
437 // int32_t, but BPF instructions always operate on unsigned quantities. We | 440 // int32_t, but BPF instructions always operate on unsigned quantities. We |
438 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, | 441 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, |
(...skipping 17 matching lines...) Expand all Loading... |
456 // "oldErr" should at this point be the "default" policy for all system call | 459 // "oldErr" should at this point be the "default" policy for all system call |
457 // numbers that don't have an explicit handler in the system call evaluator. | 460 // numbers that don't have an explicit handler in the system call evaluator. |
458 // But as we are quite paranoid, we perform some more sanity checks to verify | 461 // But as we are quite paranoid, we perform some more sanity checks to verify |
459 // that there actually is a consistent "default" policy in the first place. | 462 // that there actually is a consistent "default" policy in the first place. |
460 // We don't actually iterate over all possible 2^32 values, though. We just | 463 // We don't actually iterate over all possible 2^32 values, though. We just |
461 // perform spot checks at the boundaries. | 464 // perform spot checks at the boundaries. |
462 // The cases that we test are: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF. | 465 // The cases that we test are: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF. |
463 if (oldErr != evaluateSyscall(std::numeric_limits<int>::max()) || | 466 if (oldErr != evaluateSyscall(std::numeric_limits<int>::max()) || |
464 oldErr != evaluateSyscall(std::numeric_limits<int>::min()) || | 467 oldErr != evaluateSyscall(std::numeric_limits<int>::min()) || |
465 oldErr != evaluateSyscall(-1)) { | 468 oldErr != evaluateSyscall(-1)) { |
466 die("Invalid seccomp policy"); | 469 SANDBOX_DIE("Invalid seccomp policy"); |
467 } | 470 } |
468 ranges->push_back( | 471 ranges->push_back( |
469 Range(oldSysnum, std::numeric_limits<unsigned>::max(), oldErr)); | 472 Range(oldSysnum, std::numeric_limits<unsigned>::max(), oldErr)); |
470 } | 473 } |
471 | 474 |
472 void Sandbox::emitJumpStatements(Program *program, RetInsns *rets, | 475 void Sandbox::emitJumpStatements(Program *program, RetInsns *rets, |
473 Ranges::const_iterator start, | 476 Ranges::const_iterator start, |
474 Ranges::const_iterator stop) { | 477 Ranges::const_iterator stop) { |
475 // We convert the list of system call ranges into jump table that performs | 478 // We convert the list of system call ranges into jump table that performs |
476 // a binary search over the ranges. | 479 // a binary search over the ranges. |
477 // As a sanity check, we need to have at least two distinct ranges for us | 480 // As a sanity check, we need to have at least two distinct ranges for us |
478 // to be able to build a jump table. | 481 // to be able to build a jump table. |
479 if (stop - start <= 1) { | 482 if (stop - start <= 1) { |
480 die("Invalid set of system call ranges"); | 483 SANDBOX_DIE("Invalid set of system call ranges"); |
481 } | 484 } |
482 | 485 |
483 // Pick the range object that is located at the mid point of our list. | 486 // Pick the range object that is located at the mid point of our list. |
484 // We compare our system call number against the lowest valid system call | 487 // We compare our system call number against the lowest valid system call |
485 // number in this range object. If our number is lower, it is outside of | 488 // number in this range object. If our number is lower, it is outside of |
486 // this range object. If it is greater or equal, it might be inside. | 489 // this range object. If it is greater or equal, it might be inside. |
487 Ranges::const_iterator mid = start + (stop - start)/2; | 490 Ranges::const_iterator mid = start + (stop - start)/2; |
488 Program::size_type jmp = program->size(); | 491 Program::size_type jmp = program->size(); |
489 if (jmp >= SECCOMP_MAX_PROGRAM_SIZE) { | 492 if (jmp >= SECCOMP_MAX_PROGRAM_SIZE) { |
490 compiler_err: | 493 compiler_err: |
491 die("Internal compiler error; failed to compile jump table"); | 494 SANDBOX_DIE("Internal compiler error; failed to compile jump table"); |
492 } | 495 } |
493 program->push_back((struct sock_filter) | 496 program->push_back((struct sock_filter) |
494 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, mid->from, | 497 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, mid->from, |
495 // Jump targets are place-holders that will be fixed up later. | 498 // Jump targets are place-holders that will be fixed up later. |
496 0, 0)); | 499 0, 0)); |
497 | 500 |
498 // The comparison turned out to be false; i.e. our system call number is | 501 // The comparison turned out to be false; i.e. our system call number is |
499 // less than the range object at the mid point of the list. | 502 // less than the range object at the mid point of the list. |
500 if (mid - start == 1) { | 503 if (mid - start == 1) { |
501 // If we have narrowed things down to a single range object, we can | 504 // If we have narrowed things down to a single range object, we can |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
535 } | 538 } |
536 | 539 |
537 void Sandbox::emitReturnStatements(Program *program, const RetInsns& rets) { | 540 void Sandbox::emitReturnStatements(Program *program, const RetInsns& rets) { |
538 // Iterate over the list of distinct exit codes from our BPF filter | 541 // Iterate over the list of distinct exit codes from our BPF filter |
539 // program and emit the BPF_RET statements. | 542 // program and emit the BPF_RET statements. |
540 for (RetInsns::const_iterator ret_iter = rets.begin(); | 543 for (RetInsns::const_iterator ret_iter = rets.begin(); |
541 ret_iter != rets.end(); | 544 ret_iter != rets.end(); |
542 ++ret_iter) { | 545 ++ret_iter) { |
543 Program::size_type ip = program->size(); | 546 Program::size_type ip = program->size(); |
544 if (ip >= SECCOMP_MAX_PROGRAM_SIZE) { | 547 if (ip >= SECCOMP_MAX_PROGRAM_SIZE) { |
545 die("Internal compiler error; failed to compile jump table"); | 548 SANDBOX_DIE("Internal compiler error; failed to compile jump table"); |
546 } | 549 } |
547 program->push_back((struct sock_filter) | 550 program->push_back((struct sock_filter) |
548 BPF_STMT(BPF_RET+BPF_K, ret_iter->first)); | 551 BPF_STMT(BPF_RET+BPF_K, ret_iter->first)); |
549 | 552 |
550 // Iterate over the instruction pointers for the BPF_JMP instructions | 553 // Iterate over the instruction pointers for the BPF_JMP instructions |
551 // that need to be patched up. | 554 // that need to be patched up. |
552 for (std::vector<FixUp>::const_iterator insn_iter=ret_iter->second.begin(); | 555 for (std::vector<FixUp>::const_iterator insn_iter=ret_iter->second.begin(); |
553 insn_iter != ret_iter->second.end(); | 556 insn_iter != ret_iter->second.end(); |
554 ++insn_iter) { | 557 ++insn_iter) { |
555 // Jumps are always relative and they are always forward. | 558 // Jumps are always relative and they are always forward. |
556 int distance = ip - insn_iter->addr - 1; | 559 int distance = ip - insn_iter->addr - 1; |
557 if (distance < 0 || distance > 255) { | 560 if (distance < 0 || distance > 255) { |
558 die("Internal compiler error; failed to compile jump table"); | 561 SANDBOX_DIE("Internal compiler error; failed to compile jump table"); |
559 } | 562 } |
560 | 563 |
561 // Decide whether we need to patch up the "true" or the "false" jump | 564 // Decide whether we need to patch up the "true" or the "false" jump |
562 // target. | 565 // target. |
563 if (insn_iter->jt) { | 566 if (insn_iter->jt) { |
564 (*program)[insn_iter->addr].jt = distance; | 567 (*program)[insn_iter->addr].jt = distance; |
565 } else { | 568 } else { |
566 (*program)[insn_iter->addr].jf = distance; | 569 (*program)[insn_iter->addr].jf = distance; |
567 } | 570 } |
568 } | 571 } |
569 } | 572 } |
570 } | 573 } |
571 | 574 |
572 void Sandbox::sigSys(int nr, siginfo_t *info, void *void_context) { | 575 void Sandbox::sigSys(int nr, siginfo_t *info, void *void_context) { |
573 // Various sanity checks to make sure we actually received a signal | 576 // Various sanity checks to make sure we actually received a signal |
574 // triggered by a BPF filter. If something else triggered SIGSYS | 577 // triggered by a BPF filter. If something else triggered SIGSYS |
575 // (e.g. kill()), there is really nothing we can do with this signal. | 578 // (e.g. kill()), there is really nothing we can do with this signal. |
576 if (nr != SIGSYS || info->si_code != SYS_SECCOMP || !void_context || | 579 if (nr != SIGSYS || info->si_code != SYS_SECCOMP || !void_context || |
577 info->si_errno <= 0 || | 580 info->si_errno <= 0 || |
578 static_cast<size_t>(info->si_errno) > trapArraySize_) { | 581 static_cast<size_t>(info->si_errno) > trapArraySize_) { |
579 // die() can call LOG(FATAL). This is not normally async-signal safe | 582 // SANDBOX_DIE() can call LOG(FATAL). This is not normally async-signal safe |
580 // and can lead to bugs. We should eventually implement a different | 583 // and can lead to bugs. We should eventually implement a different |
581 // logging and reporting mechanism that is safe to be called from | 584 // logging and reporting mechanism that is safe to be called from |
582 // the sigSys() handler. | 585 // the sigSys() handler. |
583 // TODO: If we feel confident that our code otherwise works correctly, we | 586 // TODO: If we feel confident that our code otherwise works correctly, we |
584 // could actually make an argument that spurious SIGSYS should | 587 // could actually make an argument that spurious SIGSYS should |
585 // just get silently ignored. TBD | 588 // just get silently ignored. TBD |
586 sigsys_err: | 589 sigsys_err: |
587 die("Unexpected SIGSYS received"); | 590 SANDBOX_DIE("Unexpected SIGSYS received"); |
588 } | 591 } |
589 | 592 |
590 // Signal handlers should always preserve "errno". Otherwise, we could | 593 // Signal handlers should always preserve "errno". Otherwise, we could |
591 // trigger really subtle bugs. | 594 // trigger really subtle bugs. |
592 int old_errno = errno; | 595 int old_errno = errno; |
593 | 596 |
594 // Obtain the signal context. This, most notably, gives us access to | 597 // Obtain the signal context. This, most notably, gives us access to |
595 // all CPU registers at the time of the signal. | 598 // all CPU registers at the time of the signal. |
596 ucontext_t *ctx = reinterpret_cast<ucontext_t *>(void_context); | 599 ucontext_t *ctx = reinterpret_cast<ucontext_t *>(void_context); |
597 | 600 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
633 // Update the CPU register that stores the return code of the system call | 636 // Update the CPU register that stores the return code of the system call |
634 // that we just handled, and restore "errno" to the value that it had | 637 // that we just handled, and restore "errno" to the value that it had |
635 // before entering the signal handler. | 638 // before entering the signal handler. |
636 SECCOMP_RESULT(ctx) = static_cast<greg_t>(rc); | 639 SECCOMP_RESULT(ctx) = static_cast<greg_t>(rc); |
637 errno = old_errno; | 640 errno = old_errno; |
638 | 641 |
639 return; | 642 return; |
640 } | 643 } |
641 | 644 |
642 intptr_t Sandbox::bpfFailure(const struct arch_seccomp_data&, void *aux) { | 645 intptr_t Sandbox::bpfFailure(const struct arch_seccomp_data&, void *aux) { |
643 die(static_cast<char *>(aux)); | 646 SANDBOX_DIE(static_cast<char *>(aux)); |
644 } | 647 } |
645 | 648 |
646 int Sandbox::getTrapId(Sandbox::TrapFnc fnc, const void *aux) { | 649 int Sandbox::getTrapId(Sandbox::TrapFnc fnc, const void *aux) { |
647 // Each unique pair of TrapFnc and auxiliary data make up a distinct instance | 650 // Each unique pair of TrapFnc and auxiliary data make up a distinct instance |
648 // of a SECCOMP_RET_TRAP. | 651 // of a SECCOMP_RET_TRAP. |
649 std::pair<TrapFnc, const void *> key(fnc, aux); | 652 std::pair<TrapFnc, const void *> key(fnc, aux); |
650 TrapIds::const_iterator iter = trapIds_.find(key); | 653 TrapIds::const_iterator iter = trapIds_.find(key); |
651 if (iter != trapIds_.end()) { | 654 if (iter != trapIds_.end()) { |
652 // We have seen this pair before. Return the same id that we assigned | 655 // We have seen this pair before. Return the same id that we assigned |
653 // earlier. | 656 // earlier. |
654 return iter->second; | 657 return iter->second; |
655 } else { | 658 } else { |
656 // This is a new pair. Remember it and assign a new id. | 659 // This is a new pair. Remember it and assign a new id. |
657 // Please note that we have to store traps in memory that doesn't get | 660 // Please note that we have to store traps in memory that doesn't get |
658 // deallocated when the program is shutting down. A memory leak is | 661 // deallocated when the program is shutting down. A memory leak is |
659 // intentional, because we might otherwise not be able to execute | 662 // intentional, because we might otherwise not be able to execute |
660 // system calls part way through the program shutting down | 663 // system calls part way through the program shutting down |
661 if (!traps_) { | 664 if (!traps_) { |
662 traps_ = new Traps(); | 665 traps_ = new Traps(); |
663 } | 666 } |
664 Traps::size_type id = traps_->size() + 1; | 667 Traps::size_type id = traps_->size() + 1; |
665 if (id > SECCOMP_RET_DATA) { | 668 if (id > SECCOMP_RET_DATA) { |
666 // In practice, this is pretty much impossible to trigger, as there | 669 // In practice, this is pretty much impossible to trigger, as there |
667 // are other kernel limitations that restrict overall BPF program sizes. | 670 // are other kernel limitations that restrict overall BPF program sizes. |
668 die("Too many SECCOMP_RET_TRAP callback instances"); | 671 SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances"); |
669 } | 672 } |
670 | 673 |
671 traps_->push_back(ErrorCode(fnc, aux, id)); | 674 traps_->push_back(ErrorCode(fnc, aux, id)); |
672 trapIds_[key] = id; | 675 trapIds_[key] = id; |
673 | 676 |
674 // We want to access the traps_ vector from our signal handler. But | 677 // We want to access the traps_ vector from our signal handler. But |
675 // we are not assured that doing so is async-signal safe. On the other | 678 // we are not assured that doing so is async-signal safe. On the other |
676 // hand, C++ guarantees that the contents of a vector is stored in a | 679 // hand, C++ guarantees that the contents of a vector is stored in a |
677 // contiguous C-style array. | 680 // contiguous C-style array. |
678 // So, we look up the address and size of this array outside of the | 681 // So, we look up the address and size of this array outside of the |
679 // signal handler, where we can safely do so. | 682 // signal handler, where we can safely do so. |
680 trapArray_ = &(*traps_)[0]; | 683 trapArray_ = &(*traps_)[0]; |
681 trapArraySize_ = id; | 684 trapArraySize_ = id; |
682 return id; | 685 return id; |
683 } | 686 } |
684 } | 687 } |
685 | 688 |
686 bool Sandbox::dryRun_ = false; | |
687 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN; | 689 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN; |
688 int Sandbox::proc_fd_ = -1; | 690 int Sandbox::proc_fd_ = -1; |
689 Sandbox::Evaluators Sandbox::evaluators_; | 691 Sandbox::Evaluators Sandbox::evaluators_; |
690 Sandbox::Traps *Sandbox::traps_ = NULL; | 692 Sandbox::Traps *Sandbox::traps_ = NULL; |
691 Sandbox::TrapIds Sandbox::trapIds_; | 693 Sandbox::TrapIds Sandbox::trapIds_; |
692 Sandbox::ErrorCode *Sandbox::trapArray_ = NULL; | 694 Sandbox::ErrorCode *Sandbox::trapArray_ = NULL; |
693 size_t Sandbox::trapArraySize_ = 0; | 695 size_t Sandbox::trapArraySize_ = 0; |
694 | 696 |
695 } // namespace | 697 } // namespace |
OLD | NEW |