| 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 :-) |
| 11 // This is version 2 of the playground; version 1 was built on top of | 11 // This is version 2 of the playground; version 1 was built on top of |
| 12 // pre-BPF seccomp mode. | 12 // pre-BPF seccomp mode. |
| 13 namespace playground2 { | 13 namespace playground2 { |
| 14 | 14 |
| 15 // We define a really simple sandbox policy. It is just good enough for us | 15 // We define a really simple sandbox policy. It is just good enough for us |
| 16 // to tell that the sandbox has actually been activated. | 16 // to tell that the sandbox has actually been activated. |
| 17 Sandbox::ErrorCode Sandbox::probeEvaluator(int signo) { | 17 ErrorCode Sandbox::probeEvaluator(int signo) { |
| 18 switch (signo) { | 18 switch (signo) { |
| 19 case __NR_getpid: | 19 case __NR_getpid: |
| 20 // Return EPERM so that we can check that the filter actually ran. | 20 // Return EPERM so that we can check that the filter actually ran. |
| 21 return EPERM; | 21 return ErrorCode(EPERM); |
| 22 case __NR_exit_group: | 22 case __NR_exit_group: |
| 23 // Allow exit() with a non-default return code. | 23 // Allow exit() with a non-default return code. |
| 24 return SB_ALLOWED; | 24 return ErrorCode(ErrorCode::ERR_ALLOWED); |
| 25 default: | 25 default: |
| 26 // Make everything else fail in an easily recognizable way. | 26 // Make everything else fail in an easily recognizable way. |
| 27 return EINVAL; | 27 return ErrorCode(EINVAL); |
| 28 } | 28 } |
| 29 } | 29 } |
| 30 | 30 |
| 31 void Sandbox::probeProcess(void) { | 31 void Sandbox::probeProcess(void) { |
| 32 if (syscall(__NR_getpid) < 0 && errno == EPERM) { | 32 if (syscall(__NR_getpid) < 0 && errno == EPERM) { |
| 33 syscall(__NR_exit_group, (intptr_t)100); | 33 syscall(__NR_exit_group, (intptr_t)100); |
| 34 } | 34 } |
| 35 } | 35 } |
| 36 | 36 |
| 37 Sandbox::ErrorCode Sandbox::allowAllEvaluator(int signo) { | 37 ErrorCode Sandbox::allowAllEvaluator(int signo) { |
| 38 if (signo < static_cast<int>(MIN_SYSCALL) || | 38 if (signo < static_cast<int>(MIN_SYSCALL) || |
| 39 signo > static_cast<int>(MAX_SYSCALL)) { | 39 signo > static_cast<int>(MAX_SYSCALL)) { |
| 40 return ENOSYS; | 40 return ErrorCode(ENOSYS); |
| 41 } | 41 } |
| 42 return Sandbox::SB_ALLOWED; | 42 return ErrorCode(ErrorCode::ERR_ALLOWED); |
| 43 } | 43 } |
| 44 | 44 |
| 45 void Sandbox::tryVsyscallProcess(void) { | 45 void Sandbox::tryVsyscallProcess(void) { |
| 46 time_t current_time; | 46 time_t current_time; |
| 47 // time() is implemented as a vsyscall. With an older glibc, with | 47 // time() is implemented as a vsyscall. With an older glibc, with |
| 48 // vsyscall=emulate and some versions of the seccomp BPF patch | 48 // vsyscall=emulate and some versions of the seccomp BPF patch |
| 49 // we may get SIGKILL-ed. Detect this! | 49 // we may get SIGKILL-ed. Detect this! |
| 50 if (time(¤t_time) != static_cast<time_t>(-1)) { | 50 if (time(¤t_time) != static_cast<time_t>(-1)) { |
| 51 syscall(__NR_exit_group, (intptr_t)100); | 51 syscall(__NR_exit_group, (intptr_t)100); |
| 52 } | 52 } |
| 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 custom_handler(Die::LogToStderr); |
| 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 #if defined(SECCOMP_BPF_VALGRIND_HACKS) |
| 142 if (RUNNING_ON_VALGRIND) { |
| 143 // Valgrind doesn't like our run-time test. Disable testing and assume we |
| 144 // always support sandboxing. This feature should only ever be enabled when |
| 145 // debugging. |
| 146 return true; |
| 147 } |
| 148 #endif |
| 149 |
| 141 return RunFunctionInPolicy(probeProcess, Sandbox::probeEvaluator, proc_fd) && | 150 return RunFunctionInPolicy(probeProcess, Sandbox::probeEvaluator, proc_fd) && |
| 142 RunFunctionInPolicy(tryVsyscallProcess, Sandbox::allowAllEvaluator, | 151 RunFunctionInPolicy(tryVsyscallProcess, Sandbox::allowAllEvaluator, |
| 143 proc_fd); | 152 proc_fd); |
| 144 } | 153 } |
| 145 | 154 |
| 146 Sandbox::SandboxStatus Sandbox::supportsSeccompSandbox(int proc_fd) { | 155 Sandbox::SandboxStatus Sandbox::supportsSeccompSandbox(int proc_fd) { |
| 147 // It the sandbox is currently active, we clearly must have support for | 156 // It the sandbox is currently active, we clearly must have support for |
| 148 // sandboxing. | 157 // sandboxing. |
| 149 if (status_ == STATUS_ENABLED) { | 158 if (status_ == STATUS_ENABLED) { |
| 150 return status_; | 159 return status_; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 188 } | 197 } |
| 189 return status_; | 198 return status_; |
| 190 } | 199 } |
| 191 | 200 |
| 192 void Sandbox::setProcFd(int proc_fd) { | 201 void Sandbox::setProcFd(int proc_fd) { |
| 193 proc_fd_ = proc_fd; | 202 proc_fd_ = proc_fd; |
| 194 } | 203 } |
| 195 | 204 |
| 196 void Sandbox::startSandbox() { | 205 void Sandbox::startSandbox() { |
| 197 if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) { | 206 if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) { |
| 198 die("Trying to start sandbox, even though it is known to be unavailable"); | 207 SANDBOX_DIE("Trying to start sandbox, even though it is known to be " |
| 208 "unavailable"); |
| 199 } else if (status_ == STATUS_ENABLED) { | 209 } else if (status_ == STATUS_ENABLED) { |
| 200 die("Cannot start sandbox recursively. Use multiple calls to " | 210 SANDBOX_DIE("Cannot start sandbox recursively. Use multiple calls to " |
| 201 "setSandboxPolicy() to stack policies instead"); | 211 "setSandboxPolicy() to stack policies instead"); |
| 202 } | 212 } |
| 203 if (proc_fd_ < 0) { | 213 if (proc_fd_ < 0) { |
| 204 proc_fd_ = open("/proc", O_RDONLY|O_DIRECTORY); | 214 proc_fd_ = open("/proc", O_RDONLY|O_DIRECTORY); |
| 205 } | 215 } |
| 206 if (proc_fd_ < 0) { | 216 if (proc_fd_ < 0) { |
| 207 // For now, continue in degraded mode, if we can't access /proc. | 217 // For now, continue in degraded mode, if we can't access /proc. |
| 208 // In the future, we might want to tighten this requirement. | 218 // In the future, we might want to tighten this requirement. |
| 209 } | 219 } |
| 210 if (!isSingleThreaded(proc_fd_)) { | 220 if (!isSingleThreaded(proc_fd_)) { |
| 211 die("Cannot start sandbox, if process is already multi-threaded"); | 221 SANDBOX_DIE("Cannot start sandbox, if process is already multi-threaded"); |
| 212 } | 222 } |
| 213 | 223 |
| 214 // We no longer need access to any files in /proc. We want to do this | 224 // 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 | 225 // before installing the filters, just in case that our policy denies |
| 216 // close(). | 226 // close(). |
| 217 if (proc_fd_ >= 0) { | 227 if (proc_fd_ >= 0) { |
| 218 if (HANDLE_EINTR(close(proc_fd_))) { | 228 if (HANDLE_EINTR(close(proc_fd_))) { |
| 219 die("Failed to close file descriptor for /proc"); | 229 SANDBOX_DIE("Failed to close file descriptor for /proc"); |
| 220 } | 230 } |
| 221 proc_fd_ = -1; | 231 proc_fd_ = -1; |
| 222 } | 232 } |
| 223 | 233 |
| 224 // Install the filters. | 234 // Install the filters. |
| 225 installFilter(); | 235 installFilter(); |
| 226 | 236 |
| 227 // We are now inside the sandbox. | 237 // We are now inside the sandbox. |
| 228 status_ = STATUS_ENABLED; | 238 status_ = STATUS_ENABLED; |
| 229 } | 239 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 242 sb.st_nlink != 3 || | 252 sb.st_nlink != 3 || |
| 243 HANDLE_EINTR(close(task))) { | 253 HANDLE_EINTR(close(task))) { |
| 244 if (task >= 0) { | 254 if (task >= 0) { |
| 245 if (HANDLE_EINTR(close(task))) { } | 255 if (HANDLE_EINTR(close(task))) { } |
| 246 } | 256 } |
| 247 return false; | 257 return false; |
| 248 } | 258 } |
| 249 return true; | 259 return true; |
| 250 } | 260 } |
| 251 | 261 |
| 252 static bool isDenied(const Sandbox::ErrorCode& code) { | 262 bool Sandbox::isDenied(const ErrorCode& code) { |
| 253 return (code & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP || | 263 return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP || |
| 254 (code >= (SECCOMP_RET_ERRNO + 1) && | 264 (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) && |
| 255 code <= (SECCOMP_RET_ERRNO + 4095)); | 265 code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO)); |
| 256 } | 266 } |
| 257 | 267 |
| 258 void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator, | 268 void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator, |
| 259 EvaluateArguments) { | 269 EvaluateArguments) { |
| 260 // Do some sanity checks on the policy. This will warn users if they do | 270 // Do some sanity checks on the policy. This will warn users if they do |
| 261 // things that are likely unsafe and unintended. | 271 // things that are likely unsafe and unintended. |
| 262 // We also have similar checks later, when we actually compile the BPF | 272 // We also have similar checks later, when we actually compile the BPF |
| 263 // program. That catches problems with incorrectly stacked evaluators. | 273 // program. That catches problems with incorrectly stacked evaluators. |
| 264 if (!isDenied(syscallEvaluator(-1))) { | 274 if (!isDenied(syscallEvaluator(-1))) { |
| 265 die("Negative system calls should always be disallowed by policy"); | 275 SANDBOX_DIE("Negative system calls should always be disallowed by policy"); |
| 266 } | 276 } |
| 267 #ifndef NDEBUG | 277 #ifndef NDEBUG |
| 268 #if defined(__i386__) || defined(__x86_64__) | 278 #if defined(__i386__) || defined(__x86_64__) |
| 269 #if defined(__x86_64__) && defined(__ILP32__) | 279 #if defined(__x86_64__) && defined(__ILP32__) |
| 270 for (unsigned int sysnum = MIN_SYSCALL & ~0x40000000u; | 280 for (unsigned int sysnum = MIN_SYSCALL & ~0x40000000u; |
| 271 sysnum <= (MAX_SYSCALL & ~0x40000000u); | 281 sysnum <= (MAX_SYSCALL & ~0x40000000u); |
| 272 ++sysnum) { | 282 ++sysnum) { |
| 273 if (!isDenied(syscallEvaluator(sysnum))) { | 283 if (!isDenied(syscallEvaluator(sysnum))) { |
| 274 die("In x32 mode, you should not allow any non-x32 system calls"); | 284 SANDBOX_DIE("In x32 mode, you should not allow any non-x32 " |
| 285 "system calls"); |
| 275 } | 286 } |
| 276 } | 287 } |
| 277 #else | 288 #else |
| 278 for (unsigned int sysnum = MIN_SYSCALL | 0x40000000u; | 289 for (unsigned int sysnum = MIN_SYSCALL | 0x40000000u; |
| 279 sysnum <= (MAX_SYSCALL | 0x40000000u); | 290 sysnum <= (MAX_SYSCALL | 0x40000000u); |
| 280 ++sysnum) { | 291 ++sysnum) { |
| 281 if (!isDenied(syscallEvaluator(sysnum))) { | 292 if (!isDenied(syscallEvaluator(sysnum))) { |
| 282 die("x32 system calls should be explicitly disallowed"); | 293 SANDBOX_DIE("x32 system calls should be explicitly disallowed"); |
| 283 } | 294 } |
| 284 } | 295 } |
| 285 #endif | 296 #endif |
| 286 #endif | 297 #endif |
| 287 #endif | 298 #endif |
| 288 // Check interesting boundary values just outside of the valid system call | 299 // Check interesting boundary values just outside of the valid system call |
| 289 // range: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF, MIN_SYSCALL-1, MAX_SYSCALL+1. | 300 // range: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF, MIN_SYSCALL-1, MAX_SYSCALL+1. |
| 290 // They all should be denied. | 301 // They all should be denied. |
| 291 if (!isDenied(syscallEvaluator(std::numeric_limits<int>::max())) || | 302 if (!isDenied(syscallEvaluator(std::numeric_limits<int>::max())) || |
| 292 !isDenied(syscallEvaluator(std::numeric_limits<int>::min())) || | 303 !isDenied(syscallEvaluator(std::numeric_limits<int>::min())) || |
| 293 !isDenied(syscallEvaluator(-1)) || | 304 !isDenied(syscallEvaluator(-1)) || |
| 294 !isDenied(syscallEvaluator(static_cast<int>(MIN_SYSCALL) - 1)) || | 305 !isDenied(syscallEvaluator(static_cast<int>(MIN_SYSCALL) - 1)) || |
| 295 !isDenied(syscallEvaluator(static_cast<int>(MAX_SYSCALL) + 1))) { | 306 !isDenied(syscallEvaluator(static_cast<int>(MAX_SYSCALL) + 1))) { |
| 296 die("Even for default-allow policies, you must never allow system calls " | 307 SANDBOX_DIE("Even for default-allow policies, you must never allow system " |
| 297 "outside of the standard system call range"); | 308 "calls outside of the standard system call range"); |
| 298 } | 309 } |
| 299 return; | 310 return; |
| 300 } | 311 } |
| 301 | 312 |
| 302 void Sandbox::setSandboxPolicy(EvaluateSyscall syscallEvaluator, | 313 void Sandbox::setSandboxPolicy(EvaluateSyscall syscallEvaluator, |
| 303 EvaluateArguments argumentEvaluator) { | 314 EvaluateArguments argumentEvaluator) { |
| 304 if (status_ == STATUS_ENABLED) { | 315 if (status_ == STATUS_ENABLED) { |
| 305 die("Cannot change policy after sandbox has started"); | 316 SANDBOX_DIE("Cannot change policy after sandbox has started"); |
| 306 } | 317 } |
| 307 policySanityChecks(syscallEvaluator, argumentEvaluator); | 318 policySanityChecks(syscallEvaluator, argumentEvaluator); |
| 308 evaluators_.push_back(std::make_pair(syscallEvaluator, argumentEvaluator)); | 319 evaluators_.push_back(std::make_pair(syscallEvaluator, argumentEvaluator)); |
| 309 } | 320 } |
| 310 | 321 |
| 311 void Sandbox::installFilter() { | 322 void Sandbox::installFilter() { |
| 312 // Verify that the user pushed a policy. | 323 // Verify that the user pushed a policy. |
| 313 if (evaluators_.empty()) { | 324 if (evaluators_.empty()) { |
| 314 filter_failed: | 325 filter_failed: |
| 315 die("Failed to configure system call filters"); | 326 SANDBOX_DIE("Failed to configure system call filters"); |
| 316 } | 327 } |
| 317 | 328 |
| 318 // Set new SIGSYS handler | 329 // Set new SIGSYS handler |
| 319 struct sigaction sa; | 330 struct sigaction sa; |
| 320 memset(&sa, 0, sizeof(sa)); | 331 memset(&sa, 0, sizeof(sa)); |
| 321 sa.sa_sigaction = &sigSys; | 332 sa.sa_sigaction = &sigSys; |
| 322 sa.sa_flags = SA_SIGINFO; | 333 sa.sa_flags = SA_SIGINFO; |
| 323 if (sigaction(SIGSYS, &sa, NULL) < 0) { | 334 if (sigaction(SIGSYS, &sa, NULL) < 0) { |
| 324 goto filter_failed; | 335 goto filter_failed; |
| 325 } | 336 } |
| 326 | 337 |
| 327 // Unmask SIGSYS | 338 // Unmask SIGSYS |
| 328 sigset_t mask; | 339 sigset_t mask; |
| 329 if (sigemptyset(&mask) || | 340 if (sigemptyset(&mask) || |
| 330 sigaddset(&mask, SIGSYS) || | 341 sigaddset(&mask, SIGSYS) || |
| 331 sigprocmask(SIG_UNBLOCK, &mask, NULL)) { | 342 sigprocmask(SIG_UNBLOCK, &mask, NULL)) { |
| 332 goto filter_failed; | 343 goto filter_failed; |
| 333 } | 344 } |
| 334 | 345 |
| 335 // We can't handle stacked evaluators, yet. We'll get there eventually | 346 // We can't handle stacked evaluators, yet. We'll get there eventually |
| 336 // though. Hang tight. | 347 // though. Hang tight. |
| 337 if (evaluators_.size() != 1) { | 348 if (evaluators_.size() != 1) { |
| 338 die("Not implemented"); | 349 SANDBOX_DIE("Not implemented"); |
| 339 } | 350 } |
| 340 | 351 |
| 341 // Assemble the BPF filter program. | 352 // Assemble the BPF filter program. |
| 342 Program *program = new Program(); | 353 Program *program = new Program(); |
| 343 if (!program) { | 354 if (!program) { |
| 344 die("Out of memory"); | 355 SANDBOX_DIE("Out of memory"); |
| 345 } | 356 } |
| 346 | 357 |
| 347 // If the architecture doesn't match SECCOMP_ARCH, disallow the | 358 // If the architecture doesn't match SECCOMP_ARCH, disallow the |
| 348 // system call. | 359 // system call. |
| 349 program->push_back((struct sock_filter) | 360 program->push_back((struct sock_filter) |
| 350 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct arch_seccomp_data, arch))); | 361 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct arch_seccomp_data, arch))); |
| 351 program->push_back((struct sock_filter) | 362 program->push_back((struct sock_filter) |
| 352 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_ARCH, 1, 0)); | 363 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_ARCH, 1, 0)); |
| 353 | 364 |
| 354 program->push_back((struct sock_filter) | 365 program->push_back((struct sock_filter) |
| 355 BPF_STMT(BPF_RET+BPF_K, | 366 BPF_STMT(BPF_RET+BPF_K, |
| 356 ErrorCode(bpfFailure, | 367 Kill("Invalid audit architecture in BPF filter").err())); |
| 357 "Invalid audit architecture in BPF filter"))); | |
| 358 | 368 |
| 359 // Grab the system call number, so that we can implement jump tables. | 369 // Grab the system call number, so that we can implement jump tables. |
| 360 program->push_back((struct sock_filter) | 370 program->push_back((struct sock_filter) |
| 361 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct arch_seccomp_data, nr))); | 371 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct arch_seccomp_data, nr))); |
| 362 | 372 |
| 363 // On Intel architectures, verify that system call numbers are in the | 373 // On Intel architectures, verify that system call numbers are in the |
| 364 // expected number range. The older i386 and x86-64 APIs clear bit 30 | 374 // expected number range. The older i386 and x86-64 APIs clear bit 30 |
| 365 // on all system calls. The newer x86-32 API always sets bit 30. | 375 // on all system calls. The newer x86-32 API always sets bit 30. |
| 366 #if defined(__i386__) || defined(__x86_64__) | 376 #if defined(__i386__) || defined(__x86_64__) |
| 367 #if defined(__x86_64__) && defined(__ILP32__) | 377 #if defined(__x86_64__) && defined(__ILP32__) |
| 368 program->push_back((struct sock_filter) | 378 program->push_back((struct sock_filter) |
| 369 BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, 1, 0)); | 379 BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, 1, 0)); |
| 370 #else | 380 #else |
| 371 program->push_back((struct sock_filter) | 381 program->push_back((struct sock_filter) |
| 372 BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, 0, 1)); | 382 BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, 0, 1)); |
| 373 #endif | 383 #endif |
| 374 program->push_back((struct sock_filter) | 384 program->push_back((struct sock_filter) |
| 375 BPF_STMT(BPF_RET+BPF_K, | 385 BPF_STMT(BPF_RET+BPF_K, |
| 376 ErrorCode(bpfFailure, | 386 Kill("Illegal mixing of system call ABIs").err())); |
| 377 "Illegal mixing of system call ABIs"))); | |
| 378 #endif | 387 #endif |
| 379 | 388 |
| 380 | 389 |
| 381 { | 390 { |
| 382 // Evaluate all possible system calls and group their ErrorCodes into | 391 // Evaluate all possible system calls and group their ErrorCodes into |
| 383 // ranges of identical codes. | 392 // ranges of identical codes. |
| 384 Ranges ranges; | 393 Ranges ranges; |
| 385 findRanges(&ranges); | 394 findRanges(&ranges); |
| 386 | 395 |
| 387 // Compile the system call ranges to an optimized BPF program | 396 // Compile the system call ranges to an optimized BPF program |
| 388 RetInsns rets; | 397 RetInsns rets; |
| 389 emitJumpStatements(program, &rets, ranges.begin(), ranges.end()); | 398 emitJumpStatements(program, &rets, ranges.begin(), ranges.end()); |
| 390 emitReturnStatements(program, rets); | 399 emitReturnStatements(program, rets); |
| 391 } | 400 } |
| 392 | 401 |
| 393 // Make sure compilation resulted in BPF program that executes | 402 // Make sure compilation resulted in BPF program that executes |
| 394 // correctly. Otherwise, there is an internal error in our BPF compiler. | 403 // 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. | 404 // There is really nothing the caller can do until the bug is fixed. |
| 396 #ifndef NDEBUG | 405 #ifndef NDEBUG |
| 397 const char *err = NULL; | 406 const char *err = NULL; |
| 398 if (!Verifier::verifyBPF(*program, evaluators_, &err)) { | 407 if (!Verifier::verifyBPF(*program, evaluators_, &err)) { |
| 399 die(err); | 408 SANDBOX_DIE(err); |
| 400 } | 409 } |
| 401 #endif | 410 #endif |
| 402 | 411 |
| 403 // We want to be very careful in not imposing any requirements on the | 412 // 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 | 413 // 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 | 414 // 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 | 415 // be making system calls. This, for example, means we should avoid |
| 407 // using the heap and we should avoid using STL functions. | 416 // using the heap and we should avoid using STL functions. |
| 408 // Temporarily copy the contents of the "program" vector into a | 417 // Temporarily copy the contents of the "program" vector into a |
| 409 // stack-allocated array; and then explicitly destroy that object. | 418 // stack-allocated array; and then explicitly destroy that object. |
| 410 // This makes sure we don't ex- or implicitly call new/delete after we | 419 // 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 | 420 // installed the BPF filter program in the kernel. Depending on the |
| 412 // system memory allocator that is in effect, these operators can result | 421 // system memory allocator that is in effect, these operators can result |
| 413 // in system calls to things like munmap() or brk(). | 422 // in system calls to things like munmap() or brk(). |
| 414 struct sock_filter bpf[program->size()]; | 423 struct sock_filter bpf[program->size()]; |
| 415 const struct sock_fprog prog = { | 424 const struct sock_fprog prog = { |
| 416 static_cast<unsigned short>(program->size()), bpf }; | 425 static_cast<unsigned short>(program->size()), bpf }; |
| 417 memcpy(bpf, &(*program)[0], sizeof(bpf)); | 426 memcpy(bpf, &(*program)[0], sizeof(bpf)); |
| 418 delete program; | 427 delete program; |
| 419 | 428 |
| 420 // Release memory that is no longer needed | 429 // Release memory that is no longer needed |
| 421 evaluators_.clear(); | 430 evaluators_.clear(); |
| 431 errMap_.clear(); |
| 422 | 432 |
| 423 // Install BPF filter program | 433 #if defined(SECCOMP_BPF_VALGRIND_HACKS) |
| 424 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | 434 // Valgrind is really not happy about our sandbox. Disable it when running |
| 425 die(dryRun_ ? NULL : "Kernel refuses to enable no-new-privs"); | 435 // in Valgrind. This feature is dangerous and should never be enabled by |
| 426 } else { | 436 // default. We protect it behind a pre-processor option. |
| 427 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { | 437 if (!RUNNING_ON_VALGRIND) |
| 428 die(dryRun_ ? NULL : "Kernel refuses to turn on BPF filters"); | 438 #endif |
| 439 { |
| 440 // Install BPF filter program |
| 441 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { |
| 442 SANDBOX_DIE(Die::HasCustomHandler() |
| 443 ? NULL : "Kernel refuses to enable no-new-privs"); |
| 444 } else { |
| 445 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { |
| 446 SANDBOX_DIE(Die::HasCustomHandler() |
| 447 ? NULL : "Kernel refuses to turn on BPF filters"); |
| 448 } |
| 429 } | 449 } |
| 430 } | 450 } |
| 431 | 451 |
| 432 return; | 452 return; |
| 433 } | 453 } |
| 434 | 454 |
| 435 void Sandbox::findRanges(Ranges *ranges) { | 455 void Sandbox::findRanges(Ranges *ranges) { |
| 436 // Please note that "struct seccomp_data" defines system calls as a signed | 456 // Please note that "struct seccomp_data" defines system calls as a signed |
| 437 // int32_t, but BPF instructions always operate on unsigned quantities. We | 457 // int32_t, but BPF instructions always operate on unsigned quantities. We |
| 438 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, | 458 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, |
| 439 // and then verifying that the rest of the number range (both positive and | 459 // and then verifying that the rest of the number range (both positive and |
| 440 // negative) all return the same ErrorCode. | 460 // negative) all return the same ErrorCode. |
| 441 EvaluateSyscall evaluateSyscall = evaluators_.begin()->first; | 461 EvaluateSyscall evaluateSyscall = evaluators_.begin()->first; |
| 442 uint32_t oldSysnum = 0; | 462 uint32_t oldSysnum = 0; |
| 443 ErrorCode oldErr = evaluateSyscall(oldSysnum); | 463 ErrorCode oldErr = evaluateSyscall(oldSysnum); |
| 444 for (uint32_t sysnum = std::max(1u, MIN_SYSCALL); | 464 for (uint32_t sysnum = std::max(1u, MIN_SYSCALL); |
| 445 sysnum <= MAX_SYSCALL + 1; | 465 sysnum <= MAX_SYSCALL + 1; |
| 446 ++sysnum) { | 466 ++sysnum) { |
| 447 ErrorCode err = evaluateSyscall(static_cast<int>(sysnum)); | 467 ErrorCode err = evaluateSyscall(static_cast<int>(sysnum)); |
| 448 if (err != oldErr) { | 468 if (!err.Equals(oldErr)) { |
| 449 ranges->push_back(Range(oldSysnum, sysnum-1, oldErr)); | 469 ranges->push_back(Range(oldSysnum, sysnum-1, oldErr)); |
| 450 oldSysnum = sysnum; | 470 oldSysnum = sysnum; |
| 451 oldErr = err; | 471 oldErr = err; |
| 452 } | 472 } |
| 453 } | 473 } |
| 454 | 474 |
| 455 // As we looped all the way past the valid system calls (i.e. MAX_SYSCALL+1), | 475 // As we looped all the way past the valid system calls (i.e. MAX_SYSCALL+1), |
| 456 // "oldErr" should at this point be the "default" policy for all system call | 476 // "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. | 477 // 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 | 478 // 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. | 479 // 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 | 480 // We don't actually iterate over all possible 2^32 values, though. We just |
| 461 // perform spot checks at the boundaries. | 481 // perform spot checks at the boundaries. |
| 462 // The cases that we test are: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF. | 482 // The cases that we test are: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF. |
| 463 if (oldErr != evaluateSyscall(std::numeric_limits<int>::max()) || | 483 if (!oldErr.Equals(evaluateSyscall(std::numeric_limits<int>::max())) || |
| 464 oldErr != evaluateSyscall(std::numeric_limits<int>::min()) || | 484 !oldErr.Equals(evaluateSyscall(std::numeric_limits<int>::min())) || |
| 465 oldErr != evaluateSyscall(-1)) { | 485 !oldErr.Equals(evaluateSyscall(-1))) { |
| 466 die("Invalid seccomp policy"); | 486 SANDBOX_DIE("Invalid seccomp policy"); |
| 467 } | 487 } |
| 468 ranges->push_back( | 488 ranges->push_back( |
| 469 Range(oldSysnum, std::numeric_limits<unsigned>::max(), oldErr)); | 489 Range(oldSysnum, std::numeric_limits<unsigned>::max(), oldErr)); |
| 470 } | 490 } |
| 471 | 491 |
| 472 void Sandbox::emitJumpStatements(Program *program, RetInsns *rets, | 492 void Sandbox::emitJumpStatements(Program *program, RetInsns *rets, |
| 473 Ranges::const_iterator start, | 493 Ranges::const_iterator start, |
| 474 Ranges::const_iterator stop) { | 494 Ranges::const_iterator stop) { |
| 475 // We convert the list of system call ranges into jump table that performs | 495 // We convert the list of system call ranges into jump table that performs |
| 476 // a binary search over the ranges. | 496 // a binary search over the ranges. |
| 477 // As a sanity check, we need to have at least two distinct ranges for us | 497 // As a sanity check, we need to have at least two distinct ranges for us |
| 478 // to be able to build a jump table. | 498 // to be able to build a jump table. |
| 479 if (stop - start <= 1) { | 499 if (stop - start <= 1) { |
| 480 die("Invalid set of system call ranges"); | 500 SANDBOX_DIE("Invalid set of system call ranges"); |
| 481 } | 501 } |
| 482 | 502 |
| 483 // Pick the range object that is located at the mid point of our list. | 503 // 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 | 504 // 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 | 505 // 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. | 506 // this range object. If it is greater or equal, it might be inside. |
| 487 Ranges::const_iterator mid = start + (stop - start)/2; | 507 Ranges::const_iterator mid = start + (stop - start)/2; |
| 488 Program::size_type jmp = program->size(); | 508 Program::size_type jmp = program->size(); |
| 489 if (jmp >= SECCOMP_MAX_PROGRAM_SIZE) { | 509 if (jmp >= SECCOMP_MAX_PROGRAM_SIZE) { |
| 490 compiler_err: | 510 compiler_err: |
| 491 die("Internal compiler error; failed to compile jump table"); | 511 SANDBOX_DIE("Internal compiler error; failed to compile jump table"); |
| 492 } | 512 } |
| 493 program->push_back((struct sock_filter) | 513 program->push_back((struct sock_filter) |
| 494 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, mid->from, | 514 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, mid->from, |
| 495 // Jump targets are place-holders that will be fixed up later. | 515 // Jump targets are place-holders that will be fixed up later. |
| 496 0, 0)); | 516 0, 0)); |
| 497 | 517 |
| 498 // The comparison turned out to be false; i.e. our system call number is | 518 // 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. | 519 // less than the range object at the mid point of the list. |
| 500 if (mid - start == 1) { | 520 if (mid - start == 1) { |
| 501 // If we have narrowed things down to a single range object, we can | 521 // If we have narrowed things down to a single range object, we can |
| 502 // return from the BPF filter program. | 522 // return from the BPF filter program. |
| 503 // Instead of emitting a BPF_RET statement, we want to coalesce all | 523 // Instead of emitting a BPF_RET statement, we want to coalesce all |
| 504 // identical BPF_RET statements into a single instance. This results in | 524 // identical BPF_RET statements into a single instance. This results in |
| 505 // a more efficient BPF program that uses less CPU cache. | 525 // a more efficient BPF program that uses less CPU cache. |
| 506 // Since all branches in BPF programs have to be forward branches, we | 526 // Since all branches in BPF programs have to be forward branches, we |
| 507 // keep track of our current instruction pointer and then fix up the | 527 // keep track of our current instruction pointer and then fix up the |
| 508 // branch when we emit the BPF_RET statement in emitReturnStatements(). | 528 // branch when we emit the BPF_RET statement in emitReturnStatements(). |
| 509 (*rets)[start->err].push_back(FixUp(jmp, false)); | 529 (*rets)[start->err.err()].push_back(FixUp(jmp, false)); |
| 510 } else { | 530 } else { |
| 511 // Sub-divide the list of ranges and continue recursively. | 531 // Sub-divide the list of ranges and continue recursively. |
| 512 emitJumpStatements(program, rets, start, mid); | 532 emitJumpStatements(program, rets, start, mid); |
| 513 } | 533 } |
| 514 | 534 |
| 515 // The comparison turned out to be true; i.e. our system call number is | 535 // The comparison turned out to be true; i.e. our system call number is |
| 516 // greater or equal to the range object at the mid point of the list. | 536 // greater or equal to the range object at the mid point of the list. |
| 517 if (stop - mid == 1) { | 537 if (stop - mid == 1) { |
| 518 // We narrowed things down to a single range object. Remember instruction | 538 // We narrowed things down to a single range object. Remember instruction |
| 519 // pointer and exit code, so that we can patch up the target of the jump | 539 // pointer and exit code, so that we can patch up the target of the jump |
| 520 // instruction in emitReturnStatements(). | 540 // instruction in emitReturnStatements(). |
| 521 (*rets)[mid->err].push_back(FixUp(jmp, true)); | 541 (*rets)[mid->err.err()].push_back(FixUp(jmp, true)); |
| 522 } else { | 542 } else { |
| 523 // We now know where the block of instructions for the "true" comparison | 543 // We now know where the block of instructions for the "true" comparison |
| 524 // starts. Patch up the jump target of the BPF_JMP instruction that we | 544 // starts. Patch up the jump target of the BPF_JMP instruction that we |
| 525 // emitted earlier. | 545 // emitted earlier. |
| 526 int distance = program->size() - jmp - 1; | 546 int distance = program->size() - jmp - 1; |
| 527 if (distance < 0 || distance > 255) { | 547 if (distance < 0 || distance > 255) { |
| 528 goto compiler_err; | 548 goto compiler_err; |
| 529 } | 549 } |
| 530 (*program)[jmp].jt = distance; | 550 (*program)[jmp].jt = distance; |
| 531 | 551 |
| 532 // Sub-divide the list of ranges and continue recursively. | 552 // Sub-divide the list of ranges and continue recursively. |
| 533 emitJumpStatements(program, rets, mid, stop); | 553 emitJumpStatements(program, rets, mid, stop); |
| 534 } | 554 } |
| 535 } | 555 } |
| 536 | 556 |
| 537 void Sandbox::emitReturnStatements(Program *program, const RetInsns& rets) { | 557 void Sandbox::emitReturnStatements(Program *program, const RetInsns& rets) { |
| 538 // Iterate over the list of distinct exit codes from our BPF filter | 558 // Iterate over the list of distinct exit codes from our BPF filter |
| 539 // program and emit the BPF_RET statements. | 559 // program and emit the BPF_RET statements. |
| 540 for (RetInsns::const_iterator ret_iter = rets.begin(); | 560 for (RetInsns::const_iterator ret_iter = rets.begin(); |
| 541 ret_iter != rets.end(); | 561 ret_iter != rets.end(); |
| 542 ++ret_iter) { | 562 ++ret_iter) { |
| 543 Program::size_type ip = program->size(); | 563 Program::size_type ip = program->size(); |
| 544 if (ip >= SECCOMP_MAX_PROGRAM_SIZE) { | 564 if (ip >= SECCOMP_MAX_PROGRAM_SIZE) { |
| 545 die("Internal compiler error; failed to compile jump table"); | 565 SANDBOX_DIE("Internal compiler error; failed to compile jump table"); |
| 546 } | 566 } |
| 547 program->push_back((struct sock_filter) | 567 program->push_back((struct sock_filter) |
| 548 BPF_STMT(BPF_RET+BPF_K, ret_iter->first)); | 568 BPF_STMT(BPF_RET+BPF_K, ret_iter->first)); |
| 549 | 569 |
| 550 // Iterate over the instruction pointers for the BPF_JMP instructions | 570 // Iterate over the instruction pointers for the BPF_JMP instructions |
| 551 // that need to be patched up. | 571 // that need to be patched up. |
| 552 for (std::vector<FixUp>::const_iterator insn_iter=ret_iter->second.begin(); | 572 for (std::vector<FixUp>::const_iterator insn_iter=ret_iter->second.begin(); |
| 553 insn_iter != ret_iter->second.end(); | 573 insn_iter != ret_iter->second.end(); |
| 554 ++insn_iter) { | 574 ++insn_iter) { |
| 555 // Jumps are always relative and they are always forward. | 575 // Jumps are always relative and they are always forward. |
| 556 int distance = ip - insn_iter->addr - 1; | 576 int distance = ip - insn_iter->addr - 1; |
| 557 if (distance < 0 || distance > 255) { | 577 if (distance < 0 || distance > 255) { |
| 558 die("Internal compiler error; failed to compile jump table"); | 578 SANDBOX_DIE("Internal compiler error; failed to compile jump table"); |
| 559 } | 579 } |
| 560 | 580 |
| 561 // Decide whether we need to patch up the "true" or the "false" jump | 581 // Decide whether we need to patch up the "true" or the "false" jump |
| 562 // target. | 582 // target. |
| 563 if (insn_iter->jt) { | 583 if (insn_iter->jt) { |
| 564 (*program)[insn_iter->addr].jt = distance; | 584 (*program)[insn_iter->addr].jt = distance; |
| 565 } else { | 585 } else { |
| 566 (*program)[insn_iter->addr].jf = distance; | 586 (*program)[insn_iter->addr].jf = distance; |
| 567 } | 587 } |
| 568 } | 588 } |
| 569 } | 589 } |
| 570 } | 590 } |
| 571 | 591 |
| 572 void Sandbox::sigSys(int nr, siginfo_t *info, void *void_context) { | 592 void Sandbox::sigSys(int nr, siginfo_t *info, void *void_context) { |
| 573 // Various sanity checks to make sure we actually received a signal | 593 // Various sanity checks to make sure we actually received a signal |
| 574 // triggered by a BPF filter. If something else triggered SIGSYS | 594 // triggered by a BPF filter. If something else triggered SIGSYS |
| 575 // (e.g. kill()), there is really nothing we can do with this signal. | 595 // (e.g. kill()), there is really nothing we can do with this signal. |
| 576 if (nr != SIGSYS || info->si_code != SYS_SECCOMP || !void_context || | 596 if (nr != SIGSYS || info->si_code != SYS_SECCOMP || !void_context || |
| 577 info->si_errno <= 0 || | 597 info->si_errno <= 0 || |
| 578 static_cast<size_t>(info->si_errno) > trapArraySize_) { | 598 static_cast<size_t>(info->si_errno) > trapArraySize_) { |
| 579 // die() can call LOG(FATAL). This is not normally async-signal safe | 599 // SANDBOX_DIE() can call LOG(FATAL). This is not normally async-signal |
| 580 // and can lead to bugs. We should eventually implement a different | 600 // safe and can lead to bugs. We should eventually implement a different |
| 581 // logging and reporting mechanism that is safe to be called from | 601 // logging and reporting mechanism that is safe to be called from |
| 582 // the sigSys() handler. | 602 // the sigSys() handler. |
| 583 // TODO: If we feel confident that our code otherwise works correctly, we | 603 // TODO: If we feel confident that our code otherwise works correctly, we |
| 584 // could actually make an argument that spurious SIGSYS should | 604 // could actually make an argument that spurious SIGSYS should |
| 585 // just get silently ignored. TBD | 605 // just get silently ignored. TBD |
| 586 sigsys_err: | 606 sigsys_err: |
| 587 die("Unexpected SIGSYS received"); | 607 SANDBOX_DIE("Unexpected SIGSYS received"); |
| 588 } | 608 } |
| 589 | 609 |
| 590 // Signal handlers should always preserve "errno". Otherwise, we could | 610 // Signal handlers should always preserve "errno". Otherwise, we could |
| 591 // trigger really subtle bugs. | 611 // trigger really subtle bugs. |
| 592 int old_errno = errno; | 612 int old_errno = errno; |
| 593 | 613 |
| 594 // Obtain the signal context. This, most notably, gives us access to | 614 // Obtain the signal context. This, most notably, gives us access to |
| 595 // all CPU registers at the time of the signal. | 615 // all CPU registers at the time of the signal. |
| 596 ucontext_t *ctx = reinterpret_cast<ucontext_t *>(void_context); | 616 ucontext_t *ctx = reinterpret_cast<ucontext_t *>(void_context); |
| 597 | 617 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 632 | 652 |
| 633 // Update the CPU register that stores the return code of the system call | 653 // 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 | 654 // that we just handled, and restore "errno" to the value that it had |
| 635 // before entering the signal handler. | 655 // before entering the signal handler. |
| 636 SECCOMP_RESULT(ctx) = static_cast<greg_t>(rc); | 656 SECCOMP_RESULT(ctx) = static_cast<greg_t>(rc); |
| 637 errno = old_errno; | 657 errno = old_errno; |
| 638 | 658 |
| 639 return; | 659 return; |
| 640 } | 660 } |
| 641 | 661 |
| 642 intptr_t Sandbox::bpfFailure(const struct arch_seccomp_data&, void *aux) { | 662 ErrorCode Sandbox::Trap(ErrorCode::TrapFnc fnc, const void *aux) { |
| 643 die(static_cast<char *>(aux)); | |
| 644 } | |
| 645 | |
| 646 int Sandbox::getTrapId(Sandbox::TrapFnc fnc, const void *aux) { | |
| 647 // Each unique pair of TrapFnc and auxiliary data make up a distinct instance | 663 // Each unique pair of TrapFnc and auxiliary data make up a distinct instance |
| 648 // of a SECCOMP_RET_TRAP. | 664 // of a SECCOMP_RET_TRAP. |
| 649 std::pair<TrapFnc, const void *> key(fnc, aux); | 665 std::pair<ErrorCode::TrapFnc, const void *> key(fnc, aux); |
| 650 TrapIds::const_iterator iter = trapIds_.find(key); | 666 TrapIds::const_iterator iter = trapIds_.find(key); |
| 667 uint16_t id; |
| 651 if (iter != trapIds_.end()) { | 668 if (iter != trapIds_.end()) { |
| 652 // We have seen this pair before. Return the same id that we assigned | 669 // We have seen this pair before. Return the same id that we assigned |
| 653 // earlier. | 670 // earlier. |
| 654 return iter->second; | 671 id = iter->second; |
| 655 } else { | 672 } else { |
| 656 // This is a new pair. Remember it and assign a new id. | 673 // 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 | 674 // 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 | 675 // deallocated when the program is shutting down. A memory leak is |
| 659 // intentional, because we might otherwise not be able to execute | 676 // intentional, because we might otherwise not be able to execute |
| 660 // system calls part way through the program shutting down | 677 // system calls part way through the program shutting down |
| 661 if (!traps_) { | 678 if (!traps_) { |
| 662 traps_ = new Traps(); | 679 traps_ = new Traps(); |
| 663 } | 680 } |
| 664 Traps::size_type id = traps_->size() + 1; | 681 if (traps_->size() >= SECCOMP_RET_DATA) { |
| 665 if (id > SECCOMP_RET_DATA) { | |
| 666 // In practice, this is pretty much impossible to trigger, as there | 682 // In practice, this is pretty much impossible to trigger, as there |
| 667 // are other kernel limitations that restrict overall BPF program sizes. | 683 // are other kernel limitations that restrict overall BPF program sizes. |
| 668 die("Too many SECCOMP_RET_TRAP callback instances"); | 684 SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances"); |
| 669 } | 685 } |
| 686 id = traps_->size() + 1; |
| 670 | 687 |
| 671 traps_->push_back(ErrorCode(fnc, aux, id)); | 688 traps_->push_back(ErrorCode(fnc, aux, id)); |
| 672 trapIds_[key] = id; | 689 trapIds_[key] = id; |
| 673 | 690 |
| 674 // We want to access the traps_ vector from our signal handler. But | 691 // 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 | 692 // 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 | 693 // hand, C++ guarantees that the contents of a vector is stored in a |
| 677 // contiguous C-style array. | 694 // contiguous C-style array. |
| 678 // So, we look up the address and size of this array outside of the | 695 // So, we look up the address and size of this array outside of the |
| 679 // signal handler, where we can safely do so. | 696 // signal handler, where we can safely do so. |
| 680 trapArray_ = &(*traps_)[0]; | 697 trapArray_ = &(*traps_)[0]; |
| 681 trapArraySize_ = id; | 698 trapArraySize_ = id; |
| 682 return id; | |
| 683 } | 699 } |
| 700 |
| 701 ErrorCode err = ErrorCode(fnc, aux, id); |
| 702 return errMap_[err.err()] = err; |
| 684 } | 703 } |
| 685 | 704 |
| 686 bool Sandbox::dryRun_ = false; | 705 intptr_t Sandbox::bpfFailure(const struct arch_seccomp_data&, void *aux) { |
| 706 SANDBOX_DIE(static_cast<char *>(aux)); |
| 707 } |
| 708 |
| 709 ErrorCode Sandbox::Kill(const char *msg) { |
| 710 return Trap(bpfFailure, const_cast<char *>(msg)); |
| 711 } |
| 712 |
| 687 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN; | 713 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN; |
| 688 int Sandbox::proc_fd_ = -1; | 714 int Sandbox::proc_fd_ = -1; |
| 689 Sandbox::Evaluators Sandbox::evaluators_; | 715 Sandbox::Evaluators Sandbox::evaluators_; |
| 716 Sandbox::ErrMap Sandbox::errMap_; |
| 690 Sandbox::Traps *Sandbox::traps_ = NULL; | 717 Sandbox::Traps *Sandbox::traps_ = NULL; |
| 691 Sandbox::TrapIds Sandbox::trapIds_; | 718 Sandbox::TrapIds Sandbox::trapIds_; |
| 692 Sandbox::ErrorCode *Sandbox::trapArray_ = NULL; | 719 ErrorCode *Sandbox::trapArray_ = NULL; |
| 693 size_t Sandbox::trapArraySize_ = 0; | 720 size_t Sandbox::trapArraySize_ = 0; |
| 694 | 721 |
| 695 } // namespace | 722 } // namespace |
| OLD | NEW |