tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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