Sandbox.cpp (30312B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "Sandbox.h" 8 9 #include "LinuxSched.h" 10 #include "SandboxBrokerClient.h" 11 #include "SandboxChrootProto.h" 12 #include "SandboxFilter.h" 13 #include "SandboxInternal.h" 14 #include "SandboxOpenedFiles.h" 15 #include "SandboxReporterClient.h" 16 17 #include "SandboxProfilerChild.h" 18 #include "SandboxLogging.h" 19 20 #include <dirent.h> 21 #ifdef NIGHTLY_BUILD 22 # include "dlfcn.h" 23 #endif 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <linux/futex.h> 27 #include <pthread.h> 28 #include <signal.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <sys/mman.h> 33 #include <sys/prctl.h> 34 #include <sys/ptrace.h> 35 #include <sys/syscall.h> 36 #include <sys/time.h> 37 #include <unistd.h> 38 39 #include "mozilla/Array.h" 40 #include "mozilla/Atomics.h" 41 #include "mozilla/Attributes.h" 42 #include "mozilla/SandboxInfo.h" 43 #include "mozilla/StackWalk.h" 44 #include "mozilla/UniquePtr.h" 45 #include "mozilla/ipc/UtilityProcessSandboxing.h" 46 #include "prenv.h" 47 #include "base/posix/eintr_wrapper.h" 48 #include "sandbox/linux/bpf_dsl/bpf_dsl.h" 49 #include "sandbox/linux/bpf_dsl/codegen.h" 50 #include "sandbox/linux/bpf_dsl/dump_bpf.h" 51 #include "sandbox/linux/bpf_dsl/policy.h" 52 #include "sandbox/linux/bpf_dsl/policy_compiler.h" 53 #include "sandbox/linux/bpf_dsl/seccomp_macros.h" 54 #include "sandbox/linux/seccomp-bpf/trap.h" 55 #include "sandbox/linux/system_headers/linux_filter.h" 56 #include "sandbox/linux/system_headers/linux_seccomp.h" 57 #include "sandbox/linux/system_headers/linux_syscalls.h" 58 #if defined(ANDROID) 59 # include "sandbox/linux/system_headers/linux_ucontext.h" 60 #endif 61 62 #ifndef SECCOMP_FILTER_FLAG_SPEC_ALLOW 63 # define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2) 64 #endif 65 66 #ifdef MOZ_ASAN 67 // Copy libsanitizer declarations to avoid depending on ASAN headers. 68 // See also bug 1081242 comment #4. 69 extern "C" { 70 namespace __sanitizer { 71 // Win64 uses long long, but this is Linux. 72 typedef signed long sptr; 73 } // namespace __sanitizer 74 75 typedef struct { 76 int coverage_sandboxed; 77 __sanitizer::sptr coverage_fd; 78 unsigned int coverage_max_block_size; 79 } __sanitizer_sandbox_arguments; 80 81 MOZ_IMPORT_API void __sanitizer_sandbox_on_notify( 82 __sanitizer_sandbox_arguments* args); 83 } // extern "C" 84 #endif // MOZ_ASAN 85 86 // Signal number used to enable seccomp on each thread. 87 mozilla::Atomic<int> gSeccompTsyncBroadcastSignum(0); 88 89 namespace mozilla { 90 91 static mozilla::Atomic<bool> gSandboxCrashOnError(false); 92 93 // This is initialized by SandboxSetCrashFunc(). 94 SandboxCrashFunc gSandboxCrashFunc; 95 96 static int gSandboxChrootClientFd = -1; 97 static int gSandboxReporterFd = -1; 98 99 static SandboxReporterClient* gSandboxReporterClient; 100 static void (*gChromiumSigSysHandler)(int, siginfo_t*, void*); 101 102 static int TakeSandboxReporterFd() { 103 MOZ_RELEASE_ASSERT(gSandboxReporterFd != -1); 104 return std::exchange(gSandboxReporterFd, -1); 105 } 106 107 // Test whether a ucontext, interpreted as the state after a syscall, 108 // indicates the given error. See also sandbox::Syscall::PutValueInUcontext. 109 static bool ContextIsError(const ucontext_t* aContext, int aError) { 110 // Avoid integer promotion warnings. (The unary addition makes 111 // the decltype not evaluate to a reference type.) 112 typedef decltype(+SECCOMP_RESULT(aContext)) reg_t; 113 114 #ifdef __mips__ 115 return SECCOMP_PARM4(aContext) != 0 && 116 SECCOMP_RESULT(aContext) == static_cast<reg_t>(aError); 117 #else 118 return SECCOMP_RESULT(aContext) == static_cast<reg_t>(-aError); 119 #endif 120 } 121 122 /** 123 * This is the SIGSYS handler function. It delegates to the Chromium 124 * TrapRegistry handler (see InstallSigSysHandler, below) and, if the 125 * trap handler installed by the policy would fail with ENOSYS, 126 * crashes the process. This allows unintentional policy failures to 127 * be reported as crash dumps and fixed. It also logs information 128 * about the failed system call. 129 * 130 * Note that this could be invoked in parallel on multiple threads and 131 * that it could be in async signal context (e.g., intercepting an 132 * open() called from an async signal handler). 133 */ 134 MOZ_NEVER_INLINE static void SigSysHandler(int nr, siginfo_t* info, 135 void* void_context) { 136 ucontext_t* ctx = static_cast<ucontext_t*>(void_context); 137 // This shouldn't ever be null, but the Chromium handler checks for 138 // that and refrains from crashing, so let's not crash release builds: 139 MOZ_DIAGNOSTIC_ASSERT(ctx); 140 if (!ctx) { 141 return; 142 } 143 144 #if defined(DEBUG) 145 AutoForbidSignalContext sigContext; 146 #endif // defined(DEBUG) 147 148 // Save a copy of the context before invoking the trap handler, 149 // which will overwrite one or more registers with the return value. 150 ucontext_t savedCtx = *ctx; 151 152 gChromiumSigSysHandler(nr, info, ctx); 153 if (!ContextIsError(ctx, ENOSYS)) { 154 return; 155 } 156 157 SandboxReport report = gSandboxReporterClient->MakeReportAndSend(&savedCtx); 158 159 // TODO, someday when this is enabled on MIPS: include the two extra 160 // args in the error message. 161 SANDBOX_LOG( 162 "seccomp sandbox violation: pid %d, tid %d, syscall %d," 163 " args %d %d %d %d %d %d.%s", 164 report.mPid, report.mTid, report.mSyscall, report.mArgs[0], 165 report.mArgs[1], report.mArgs[2], report.mArgs[3], report.mArgs[4], 166 report.mArgs[5], gSandboxCrashOnError ? " Killing process." : ""); 167 168 if (gSandboxCrashOnError) { 169 // Bug 1017393: record syscall number somewhere useful. 170 info->si_addr = reinterpret_cast<void*>(report.mSyscall); 171 172 gSandboxCrashFunc(nr, info, &savedCtx, CallerPC()); 173 _exit(127); 174 } 175 } 176 177 /** 178 * This function installs the SIGSYS handler. This is slightly 179 * complicated because we want to use Chromium's handler to dispatch 180 * to specific trap handlers defined in the policy, but we also need 181 * the full original signal context to give to Breakpad for crash 182 * dumps. So we install Chromium's handler first, then retrieve its 183 * address so our replacement can delegate to it. 184 */ 185 static void InstallSigSysHandler(void) { 186 struct sigaction act; 187 188 // Ensure that the Chromium handler is installed. 189 (void)sandbox::Trap::Registry(); 190 191 // If the signal handling state isn't as expected, crash now instead 192 // of crashing later (and more confusingly) when SIGSYS happens. 193 194 if (sigaction(SIGSYS, nullptr, &act) != 0) { 195 MOZ_CRASH("Couldn't read old SIGSYS disposition"); 196 } 197 if ((act.sa_flags & SA_SIGINFO) != SA_SIGINFO) { 198 MOZ_CRASH("SIGSYS not already set to a siginfo handler?"); 199 } 200 MOZ_RELEASE_ASSERT(act.sa_sigaction); 201 gChromiumSigSysHandler = act.sa_sigaction; 202 act.sa_sigaction = SigSysHandler; 203 // Currently, SA_NODEFER should already be set by the Chromium code, 204 // but it's harmless to ensure that it's set: 205 MOZ_ASSERT(act.sa_flags & SA_NODEFER); 206 act.sa_flags |= SA_NODEFER; 207 if (sigaction(SIGSYS, &act, nullptr) < 0) { 208 MOZ_CRASH("Couldn't change SIGSYS disposition"); 209 } 210 } 211 212 /** 213 * This function installs the syscall filter, a.k.a. seccomp. The 214 * aUseTSync flag indicates whether this should apply to all threads 215 * in the process -- which will fail if the kernel doesn't support 216 * that -- or only the current thread. 217 * 218 * SECCOMP_MODE_FILTER is the "bpf" mode of seccomp which allows 219 * to pass a bpf program (in our case, it contains a syscall 220 * whitelist). 221 * 222 * PR_SET_NO_NEW_PRIVS ensures that it is impossible to grant more 223 * syscalls to the process beyond this point (even after fork()), and 224 * prevents gaining capabilities (e.g., by exec'ing a setuid root 225 * program). The kernel won't allow seccomp-bpf without doing this, 226 * because otherwise it could be used for privilege escalation attacks. 227 * 228 * Returns false if the filter was already installed (see the 229 * PR_SET_NO_NEW_PRIVS rule in SandboxFilter.cpp). Crashes on any 230 * other error condition. 231 * 232 * @see SandboxInfo 233 * @see BroadcastSetThreadSandbox 234 */ 235 [[nodiscard]] static bool InstallSyscallFilter(const sock_fprog* aProg, 236 bool aUseTSync) { 237 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { 238 if (!aUseTSync && errno == ETXTBSY) { 239 return false; 240 } 241 SANDBOX_LOG_ERRNO("prctl(PR_SET_NO_NEW_PRIVS) failed"); 242 MOZ_CRASH("prctl(PR_SET_NO_NEW_PRIVS)"); 243 } 244 245 if (aUseTSync) { 246 // Try with SECCOMP_FILTER_FLAGS_SPEC_ALLOW, and then without if 247 // that fails with EINVAL (or if an env var is set, for testing 248 // purposes). 249 // 250 // Context: Linux 4.17 applied some Spectre mitigations (SSBD and 251 // STIBP) by default when seccomp-bpf is used, but also added that 252 // flag to opt out (and also sysadmin-level overrides). Later, 253 // Linux 5.16 turned them off by default; the rationale seems to 254 // be, roughly: the attacks are impractical or were already 255 // mitigated in other ways, there are worse attacks that these 256 // measures don't stop, and the performance impact is severe 257 // enough that container software was already opting out. 258 // 259 // For the full rationale, see 260 // https://github.com/torvalds/linux/commit/2f46993d83ff4abb310e 261 // 262 // In our case, STIBP causes a noticeable performance hit: WASM 263 // microbenchmarks of indirect calls regress by up to 2x or 3x 264 // depending on CPU. Given that upstream Linux has changed the 265 // default years ago, we opt out. 266 267 static const bool kSpecAllow = !PR_GetEnv("MOZ_SANDBOX_NO_SPEC_ALLOW"); 268 269 const auto setSeccomp = [aProg](int aFlags) -> long { 270 return syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 271 SECCOMP_FILTER_FLAG_TSYNC | aFlags, aProg); 272 }; 273 274 long rv; 275 if (kSpecAllow) { 276 rv = setSeccomp(SECCOMP_FILTER_FLAG_SPEC_ALLOW); 277 } else { 278 rv = -1; 279 errno = EINVAL; 280 } 281 if (rv != 0 && errno == EINVAL) { 282 rv = setSeccomp(0); 283 } 284 if (rv != 0) { 285 SANDBOX_LOG_ERRNO("thread-synchronized seccomp failed"); 286 MOZ_CRASH("seccomp+tsync failed, but kernel supports tsync"); 287 } 288 } else { 289 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, (unsigned long)aProg, 0, 290 0)) { 291 SANDBOX_LOG_ERRNO("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER) failed"); 292 MOZ_CRASH("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER)"); 293 } 294 } 295 return true; 296 } 297 298 // Use signals for permissions that need to be set per-thread. 299 // The communication channel from the signal handler back to the main thread. 300 static mozilla::Atomic<int> gSetSandboxDone; 301 // Pass the filter itself through a global. 302 const sock_fprog* gSetSandboxFilter; 303 304 // We have to dynamically allocate the signal number; see bug 1038900. 305 // This function returns the first realtime signal currently set to 306 // default handling (i.e., not in use), or 0 if none could be found. 307 // 308 // WARNING: if this function or anything similar to it (including in 309 // external libraries) is used on multiple threads concurrently, there 310 // will be a race condition. 311 static int FindFreeSignalNumber() { 312 for (int signum = SIGRTMAX; signum >= SIGRTMIN; --signum) { 313 struct sigaction sa; 314 315 if (sigaction(signum, nullptr, &sa) == 0 && 316 (sa.sa_flags & SA_SIGINFO) == 0 && sa.sa_handler == SIG_DFL) { 317 return signum; 318 } 319 } 320 return 0; 321 } 322 323 // Returns true if sandboxing was enabled, or false if sandboxing 324 // already was enabled. Crashes if sandboxing could not be enabled. 325 static bool SetThreadSandbox() { 326 return InstallSyscallFilter(gSetSandboxFilter, false); 327 } 328 329 static void SetThreadSandboxHandler(int signum) { 330 // The non-zero number sent back to the main thread indicates 331 // whether action was taken. 332 if (SetThreadSandbox()) { 333 gSetSandboxDone = 2; 334 } else { 335 gSetSandboxDone = 1; 336 } 337 // Wake up the main thread. See the FUTEX_WAIT call, below, for an 338 // explanation. 339 syscall(__NR_futex, reinterpret_cast<int*>(&gSetSandboxDone), FUTEX_WAKE, 1); 340 } 341 342 static void EnterChroot() { 343 const char* env = PR_GetEnv(kSandboxChrootEnvFlag); 344 if (!env || !*env || *env == '0') { 345 return; 346 } 347 char msg = kSandboxChrootRequest; 348 ssize_t msg_len = HANDLE_EINTR(write(gSandboxChrootClientFd, &msg, 1)); 349 MOZ_RELEASE_ASSERT(msg_len == 1); 350 msg_len = HANDLE_EINTR(read(gSandboxChrootClientFd, &msg, 1)); 351 MOZ_RELEASE_ASSERT(msg_len == 1); 352 MOZ_RELEASE_ASSERT(msg == kSandboxChrootResponse); 353 close(gSandboxChrootClientFd); 354 gSandboxChrootClientFd = -1; 355 } 356 357 static void BroadcastSetThreadSandbox(const sock_fprog* aFilter) { 358 pid_t pid, tid, myTid; 359 DIR* taskdp; 360 struct dirent* de; 361 362 // This function does not own *aFilter, so this global needs to 363 // always be zeroed before returning. 364 gSetSandboxFilter = aFilter; 365 366 static_assert(sizeof(mozilla::Atomic<int>) == sizeof(int), 367 "mozilla::Atomic<int> isn't represented by an int"); 368 pid = getpid(); 369 myTid = syscall(__NR_gettid); 370 taskdp = opendir("/proc/self/task"); 371 if (taskdp == nullptr) { 372 SANDBOX_LOG_ERRNO("opendir /proc/self/task"); 373 MOZ_CRASH("failed while trying to open directory /proc/self/task"); 374 } 375 376 // In case this races with a not-yet-deprivileged thread cloning 377 // itself, repeat iterating over all threads until we find none 378 // that are still privileged. 379 bool sandboxProgress; 380 const int tsyncSignum = gSeccompTsyncBroadcastSignum; 381 do { 382 sandboxProgress = false; 383 // For each thread... 384 while ((de = readdir(taskdp))) { 385 char* endptr; 386 tid = strtol(de->d_name, &endptr, 10); 387 if (*endptr != '\0' || tid <= 0) { 388 // Not a task ID. 389 continue; 390 } 391 if (tid == myTid) { 392 // Drop this thread's privileges last, below, so we can 393 // continue to signal other threads. 394 continue; 395 } 396 397 MOZ_RELEASE_ASSERT(tsyncSignum != 0); 398 399 // Reset the futex cell and signal. 400 gSetSandboxDone = 0; 401 if (syscall(__NR_tgkill, pid, tid, tsyncSignum) != 0) { 402 if (errno == ESRCH) { 403 SANDBOX_LOG("Thread %d unexpectedly exited.", tid); 404 // Rescan threads, in case it forked before exiting. 405 sandboxProgress = true; 406 continue; 407 } 408 SANDBOX_LOG_ERRNO("tgkill(%d,%d)", pid, tid); 409 MOZ_CRASH("failed while trying to send a signal to a thread"); 410 } 411 // It's unlikely, but if the thread somehow manages to exit 412 // after receiving the signal but before entering the signal 413 // handler, we need to avoid blocking forever. 414 // 415 // Using futex directly lets the signal handler send the wakeup 416 // from an async signal handler (pthread mutex/condvar calls 417 // aren't allowed), and to use a relative timeout that isn't 418 // affected by changes to the system clock (not possible with 419 // POSIX semaphores). 420 // 421 // If a thread doesn't respond within a reasonable amount of 422 // time, but still exists, we crash -- the alternative is either 423 // blocking forever or silently losing security, and it 424 // shouldn't actually happen. 425 static const int crashDelay = 10; // seconds 426 struct timespec timeLimit; 427 clock_gettime(CLOCK_MONOTONIC, &timeLimit); 428 timeLimit.tv_sec += crashDelay; 429 while (true) { 430 static const struct timespec futexTimeout = {0, 431 10 * 1000 * 1000}; // 10ms 432 // Atomically: if gSetSandboxDone == 0, then sleep. 433 if (syscall(__NR_futex, reinterpret_cast<int*>(&gSetSandboxDone), 434 FUTEX_WAIT, 0, &futexTimeout) != 0) { 435 if (errno != EWOULDBLOCK && errno != ETIMEDOUT && errno != EINTR) { 436 SANDBOX_LOG_ERRNO("FUTEX_WAIT"); 437 MOZ_CRASH("failed during FUTEX_WAIT"); 438 } 439 } 440 // Did the handler finish? 441 if (gSetSandboxDone > 0) { 442 if (gSetSandboxDone == 2) { 443 sandboxProgress = true; 444 } 445 break; 446 } 447 // Has the thread ceased to exist? 448 if (syscall(__NR_tgkill, pid, tid, 0) != 0) { 449 if (errno == ESRCH) { 450 SANDBOX_LOG("Thread %d unexpectedly exited.", tid); 451 } 452 // Rescan threads, in case it forked before exiting. 453 // Also, if it somehow failed in a way that wasn't ESRCH, 454 // and still exists, that will be handled on the next pass. 455 sandboxProgress = true; 456 break; 457 } 458 struct timespec now; 459 clock_gettime(CLOCK_MONOTONIC, &now); 460 if (now.tv_sec > timeLimit.tv_sec || 461 (now.tv_sec == timeLimit.tv_sec && 462 now.tv_nsec > timeLimit.tv_nsec)) { 463 SANDBOX_LOG( 464 "Thread %d unresponsive for %d seconds." 465 " Killing process.", 466 tid, crashDelay); 467 468 MOZ_CRASH("failed while waiting for unresponsive thread"); 469 } 470 } 471 } 472 rewinddir(taskdp); 473 } while (sandboxProgress); 474 475 void (*oldHandler)(int); 476 oldHandler = signal(tsyncSignum, SIG_DFL); 477 if (oldHandler != SetThreadSandboxHandler) { 478 // See the comment on FindFreeSignalNumber about race conditions. 479 SANDBOX_LOG("handler for signal %d was changed to %p!", tsyncSignum, 480 oldHandler); 481 MOZ_CRASH("handler for the signal was changed to another"); 482 } 483 gSeccompTsyncBroadcastSignum = 0; 484 (void)closedir(taskdp); 485 // And now, deprivilege the main thread: 486 SetThreadSandbox(); 487 gSetSandboxFilter = nullptr; 488 } 489 490 static void ApplySandboxWithTSync(sock_fprog* aFilter) { 491 // At this point we're committed to using tsync, because we'd have 492 // needed to allocate a signal and prevent it from being blocked on 493 // other threads (see SandboxHooks.cpp), so there's no attempt to 494 // fall back to the non-tsync path. 495 if (!InstallSyscallFilter(aFilter, true)) { 496 MOZ_CRASH("failed while trying to install syscall filter"); 497 } 498 } 499 500 #ifdef NIGHTLY_BUILD 501 static bool IsLibPresent(const char* aName) { 502 if (const auto handle = dlopen(aName, RTLD_LAZY | RTLD_NOLOAD)) { 503 dlclose(handle); 504 return true; 505 } 506 return false; 507 } 508 509 static const Array<const char*, 1> kLibsThatWillCrash{ 510 "libesets_pac.so", 511 }; 512 #endif // NIGHTLY_BUILD 513 514 void SandboxEarlyInit(Maybe<UniqueFileHandle>&& aSandboxReporter, 515 Maybe<UniqueFileHandle>&& aChrootClient) { 516 if (!aSandboxReporter) { 517 return; 518 } 519 520 // Initialize the global sandbox reporter and chroot client FDs. 521 gSandboxReporterFd = aSandboxReporter->release(); 522 523 if (aChrootClient) { 524 gSandboxChrootClientFd = aChrootClient->release(); 525 } 526 527 // Fix LD_PRELOAD for any child processes. See bug 1434392 comment #10; 528 // this can probably go away when audio remoting is mandatory. 529 const char* oldPreload = PR_GetEnv("MOZ_ORIG_LD_PRELOAD"); 530 char* preloadEntry; 531 // This string is "leaked" because the environment takes ownership. 532 if (asprintf(&preloadEntry, "LD_PRELOAD=%s", oldPreload ? oldPreload : "") != 533 -1) { 534 PR_SetEnv(preloadEntry); 535 } 536 537 // If TSYNC is not supported, set up signal handler 538 // used to enable seccomp on each thread. 539 if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompTSync)) { 540 // The signal number has to be chosen early, so that the 541 // interceptions in SandboxHooks.cpp can prevent it from being 542 // masked. 543 const int tsyncSignum = FindFreeSignalNumber(); 544 if (tsyncSignum == 0) { 545 SANDBOX_LOG("No available signal numbers!"); 546 MOZ_CRASH("failed while trying to find a free signal number"); 547 } 548 gSeccompTsyncBroadcastSignum = tsyncSignum; 549 550 // ...and the signal handler also needs to be installed now, to 551 // indicate to anything else looking for free signals that it's 552 // claimed. 553 void (*oldHandler)(int); 554 oldHandler = signal(tsyncSignum, SetThreadSandboxHandler); 555 if (oldHandler != SIG_DFL) { 556 // See the comment on FindFreeSignalNumber about race conditions. 557 if (oldHandler == SIG_ERR) { 558 MOZ_CRASH("failed while registering the signal handler"); 559 } else { 560 MOZ_CRASH("failed because the signal is in use by another handler"); 561 } 562 SANDBOX_LOG("signal %d in use by handler %p!\n", tsyncSignum, oldHandler); 563 } 564 } 565 } 566 567 static void RunGlibcLazyInitializers() { 568 // Make glibc's lazy initialization of shm_open() run before sandboxing 569 int fd = shm_open("/dummy", O_RDONLY, 0); 570 if (fd > 0) { 571 close(fd); // In the unlikely case we actually opened something 572 } 573 } 574 575 static void SandboxLateInit() { 576 #ifdef NIGHTLY_BUILD 577 gSandboxCrashOnError = true; 578 for (const char* name : kLibsThatWillCrash) { 579 if (IsLibPresent(name)) { 580 gSandboxCrashOnError = false; 581 break; 582 } 583 } 584 #endif 585 586 if (const char* envVar = PR_GetEnv("MOZ_SANDBOX_CRASH_ON_ERROR")) { 587 if (envVar[0]) { 588 gSandboxCrashOnError = envVar[0] != '0'; 589 } 590 } 591 592 RunGlibcLazyInitializers(); 593 594 // This will run on main thread before it is in a signal-handler context, to 595 // make sure rprofiler pointers are properly initialized (and send a marker 596 // with a stack if the profiler is already running) on the main thread for 597 // later use (read-only) on other threads. 598 // 599 // If profiler is already started (e.g., MOZ_PROFILER_STARTUP=1) the following 600 // will be instantiated, but if the profiler is not yet started, then it is a 601 // no-op and rely on "profiler-started" observer from 602 // RegisterProfilerObserversForSandboxProfiler: 603 // 604 // This will create: 605 // - pointers to uprofiler to make use of the profiler 606 // - a SandboxProfiler 607 // - a BoundedMPSCQueue 608 // - a std::thread 609 // 610 // So that later usage of uprofiler under SIGSYS context can: 611 // - safely (i.e., no alloc etc.) take a stack 612 // - copy it over to the queue 613 // - thread polling from the queue in a more favorable context will be able 614 // to do what is required to finish sending to the profiler 615 616 // If the profiler is not running those are no-op 617 SandboxProfiler::Create(); 618 const void* top = CallerPC(); 619 SandboxProfiler::ReportInit(top); 620 } 621 622 // Common code for sandbox startup. 623 static void SetCurrentProcessSandbox( 624 UniquePtr<sandbox::bpf_dsl::Policy> aPolicy) { 625 MOZ_ASSERT(gSandboxCrashFunc); 626 MOZ_RELEASE_ASSERT(gSandboxReporterClient != nullptr); 627 SandboxLateInit(); 628 629 // Auto-collect child processes -- mainly the chroot helper if 630 // present, but also anything setns()ed into the pid namespace (not 631 // yet implemented). This process won't be able to waitpid them 632 // after the seccomp-bpf policy is applied. 633 signal(SIGCHLD, SIG_IGN); 634 635 // Note: PolicyCompiler borrows the policy and registry for its 636 // lifetime, but does not take ownership of them. 637 sandbox::bpf_dsl::PolicyCompiler compiler(aPolicy.get(), 638 sandbox::Trap::Registry()); 639 640 // In case of errors detected by the compiler (like ABI violations), 641 // log and crash normally; the default is SECCOMP_RET_KILL_THREAD, 642 // which results in hard-to-debug hangs. 643 compiler.SetPanicFunc([](const char* error) -> sandbox::bpf_dsl::ResultExpr { 644 // Note: this assumes that `error` is a string literal, which is 645 // currently the case for all callers. (An intentionally leaked 646 // heap allocation would also work.) 647 return sandbox::bpf_dsl::Trap( 648 [](const arch_seccomp_data&, void* aux) -> intptr_t { 649 auto error = reinterpret_cast<const char*>(aux); 650 SANDBOX_LOG("Panic: %s", error); 651 MOZ_CRASH("Sandbox Panic"); 652 // unreachable 653 }, 654 (void*)error); 655 }); 656 657 sandbox::CodeGen::Program program = compiler.Compile(); 658 if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { 659 sandbox::bpf_dsl::DumpBPF::PrintProgram(program); 660 } 661 662 InstallSigSysHandler(); 663 664 #ifdef MOZ_ASAN 665 __sanitizer_sandbox_arguments asanArgs; 666 asanArgs.coverage_sandboxed = 1; 667 asanArgs.coverage_fd = -1; 668 asanArgs.coverage_max_block_size = 0; 669 __sanitizer_sandbox_on_notify(&asanArgs); 670 #endif 671 672 // The syscall takes a C-style array, so copy the vector into one. 673 size_t programLen = program.size(); 674 UniquePtr<sock_filter[]> flatProgram(new sock_filter[programLen]); 675 for (auto i = program.begin(); i != program.end(); ++i) { 676 flatProgram[i - program.begin()] = *i; 677 } 678 679 sock_fprog fprog; 680 fprog.filter = flatProgram.get(); 681 fprog.len = static_cast<unsigned short>(programLen); 682 MOZ_RELEASE_ASSERT(static_cast<size_t>(fprog.len) == programLen); 683 684 const SandboxInfo info = SandboxInfo::Get(); 685 if (info.Test(SandboxInfo::kHasSeccompTSync)) { 686 if (info.Test(SandboxInfo::kVerbose)) { 687 SANDBOX_LOG("using seccomp tsync"); 688 } 689 ApplySandboxWithTSync(&fprog); 690 } else { 691 if (info.Test(SandboxInfo::kVerbose)) { 692 SANDBOX_LOG("no tsync support; using signal broadcast"); 693 } 694 BroadcastSetThreadSandbox(&fprog); 695 } 696 697 // Now that all threads' filesystem accesses are being intercepted 698 // (if a broker is used) it's safe to chroot the process: 699 EnterChroot(); 700 } 701 702 /** 703 * Starts the seccomp sandbox for a content process. Should be called 704 * only once, and before any potentially harmful content is loaded. 705 * 706 * Will normally make the process exit on failure. 707 */ 708 bool SetContentProcessSandbox(ContentProcessSandboxParams&& aParams) { 709 int brokerFd = aParams.mBrokerFd; 710 aParams.mBrokerFd = -1; 711 712 if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForContent)) { 713 if (brokerFd >= 0) { 714 close(brokerFd); 715 } 716 return false; 717 } 718 719 auto procType = aParams.mFileProcess ? SandboxReport::ProcType::FILE 720 : SandboxReport::ProcType::CONTENT; 721 gSandboxReporterClient = 722 new SandboxReporterClient(procType, TakeSandboxReporterFd()); 723 724 // This needs to live until the process exits. 725 static SandboxBrokerClient* sBroker; 726 if (brokerFd >= 0) { 727 sBroker = new SandboxBrokerClient(brokerFd); 728 } 729 730 SetCurrentProcessSandbox( 731 GetContentSandboxPolicy(sBroker, std::move(aParams))); 732 return true; 733 } 734 /** 735 * Starts the seccomp sandbox for a media plugin process. Should be 736 * called only once, and before any potentially harmful content is 737 * loaded -- including the plugin itself, if it's considered untrusted. 738 * 739 * The file indicated by aFilePath, if non-null, can be open()ed 740 * read-only, once, after the sandbox starts; it should be the .so 741 * file implementing the not-yet-loaded plugin. 742 * 743 * Will normally make the process exit on failure. 744 */ 745 void SetMediaPluginSandbox(const char* aFilePath) { 746 MOZ_RELEASE_ASSERT(aFilePath != nullptr); 747 if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForMedia)) { 748 return; 749 } 750 751 gSandboxReporterClient = new SandboxReporterClient( 752 SandboxReport::ProcType::MEDIA_PLUGIN, TakeSandboxReporterFd()); 753 754 SandboxOpenedFile plugin(aFilePath); 755 if (!plugin.IsOpen()) { 756 SANDBOX_LOG_ERRNO("failed to open plugin file %s", aFilePath); 757 MOZ_CRASH("failed while trying to open the plugin file "); 758 } 759 760 auto files = new SandboxOpenedFiles(); 761 files->Add(std::move(plugin)); 762 files->Add("/dev/urandom", SandboxOpenedFile::Dup::YES); 763 files->Add("/dev/random", SandboxOpenedFile::Dup::YES); 764 files->Add("/etc/ld.so.cache"); // Needed for NSS in clearkey. 765 files->Add("/sys/devices/system/cpu/cpu0/tsc_freq_khz"); 766 files->Add("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"); 767 files->Add("/proc/cpuinfo"); // Info also available via CPUID instruction. 768 files->Add("/proc/sys/crypto/fips_enabled"); // Needed for NSS in clearkey. 769 #ifdef __i386__ 770 files->Add("/proc/self/auxv"); // Info also in process's address space. 771 #endif 772 // Bug 1712506: the Widevine CDM will try to access these but 773 // doesn't appear to need them. 774 files->Add("/sys/devices/system/cpu/online", SandboxOpenedFile::Error{}); 775 files->Add("/proc/stat", SandboxOpenedFile::Error{}); 776 files->Add("/proc/net/unix", SandboxOpenedFile::Error{}); 777 files->Add("/proc/self/maps", SandboxOpenedFile::Error{}); 778 779 // Finally, start the sandbox. 780 SetCurrentProcessSandbox(GetMediaSandboxPolicy(files)); 781 } 782 783 void SetRemoteDataDecoderSandbox(int aBroker) { 784 if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompBPF) || 785 PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX")) { 786 if (aBroker >= 0) { 787 close(aBroker); 788 } 789 return; 790 } 791 792 gSandboxReporterClient = new SandboxReporterClient( 793 SandboxReport::ProcType::RDD, TakeSandboxReporterFd()); 794 795 // FIXME(bug 1513773): merge this with the one for content? 796 static SandboxBrokerClient* sBroker; 797 if (aBroker >= 0) { 798 sBroker = new SandboxBrokerClient(aBroker); 799 } 800 801 SetCurrentProcessSandbox(GetDecoderSandboxPolicy(sBroker)); 802 } 803 804 void SetSocketProcessSandbox(SocketProcessSandboxParams&& aParams) { 805 if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompBPF) || 806 PR_GetEnv("MOZ_DISABLE_SOCKET_PROCESS_SANDBOX")) { 807 return; 808 } 809 810 gSandboxReporterClient = new SandboxReporterClient( 811 SandboxReport::ProcType::SOCKET_PROCESS, TakeSandboxReporterFd()); 812 813 // FIXME(bug 1513773): merge this with the ones for content and RDD? 814 static SandboxBrokerClient* sBroker; 815 MOZ_ASSERT(!sBroker); // This should only ever be called once. 816 if (aParams.mBroker) { 817 sBroker = new SandboxBrokerClient(aParams.mBroker.release()); 818 } 819 820 SetCurrentProcessSandbox( 821 GetSocketProcessSandboxPolicy(sBroker, std::move(aParams))); 822 } 823 824 void SetUtilitySandbox(int aBroker, ipc::SandboxingKind aKind) { 825 if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompBPF) || 826 !IsUtilitySandboxEnabled(aKind)) { 827 if (aBroker >= 0) { 828 close(aBroker); 829 } 830 return; 831 } 832 833 gSandboxReporterClient = new SandboxReporterClient( 834 SandboxReport::ProcType::UTILITY, TakeSandboxReporterFd()); 835 836 static SandboxBrokerClient* sBroker; 837 if (aBroker >= 0) { 838 sBroker = new SandboxBrokerClient(aBroker); 839 } 840 841 UniquePtr<sandbox::bpf_dsl::Policy> policy; 842 switch (aKind) { 843 case ipc::SandboxingKind::GENERIC_UTILITY: 844 policy = GetUtilitySandboxPolicy(sBroker); 845 break; 846 847 default: 848 MOZ_ASSERT(false, "Invalid SandboxingKind"); 849 break; 850 } 851 852 SetCurrentProcessSandbox(std::move(policy)); 853 } 854 855 bool SetSandboxCrashOnError(bool aValue) { 856 bool oldValue = gSandboxCrashOnError; 857 gSandboxCrashOnError = aValue; 858 return oldValue; 859 } 860 861 void DestroySandboxProfiler() { SandboxProfiler::Shutdown(); } 862 863 void CreateSandboxProfiler() { SandboxProfiler::Create(); } 864 865 } // namespace mozilla