tor-browser

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

SandboxReporter.cpp (9580B)


      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 "SandboxReporter.h"
      8 #include "SandboxLogging.h"
      9 
     10 #include <algorithm>
     11 #include <errno.h>
     12 #include <sys/socket.h>
     13 #include <sys/types.h>
     14 #include <time.h>  // for clockid_t
     15 
     16 #include "GeckoProfiler.h"
     17 #include "mozilla/Assertions.h"
     18 #include "mozilla/ClearOnShutdown.h"
     19 #include "mozilla/StaticMutex.h"
     20 #include "mozilla/PodOperations.h"
     21 #include "nsThreadUtils.h"
     22 #include "mozilla/glean/SecuritySandboxMetrics.h"
     23 #include "sandbox/linux/system_headers/linux_syscalls.h"
     24 
     25 // Distinguish architectures for the telemetry key.
     26 #if defined(__i386__)
     27 #  define SANDBOX_ARCH_NAME "x86"
     28 #elif defined(__x86_64__)
     29 #  define SANDBOX_ARCH_NAME "amd64"
     30 #elif defined(__arm__)
     31 #  define SANDBOX_ARCH_NAME "arm"
     32 #elif defined(__aarch64__)
     33 #  define SANDBOX_ARCH_NAME "arm64"
     34 #else
     35 #  error "unrecognized architecture"
     36 #endif
     37 
     38 namespace mozilla {
     39 
     40 StaticAutoPtr<SandboxReporter> SandboxReporter::sSingleton;
     41 
     42 SandboxReporter::SandboxReporter()
     43    : mClientFd(-1),
     44      mServerFd(-1),
     45      mMutex("SandboxReporter"),
     46      mBuffer(MakeUnique<SandboxReport[]>(kSandboxReporterBufferSize)),
     47      mCount(0) {}
     48 
     49 bool SandboxReporter::Init() {
     50  int fds[2];
     51 
     52  if (0 != socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, fds)) {
     53    SANDBOX_LOG_ERRNO("SandboxReporter: socketpair failed");
     54    return false;
     55  }
     56  mClientFd = fds[0];
     57  mServerFd = fds[1];
     58 
     59  if (!PlatformThread::Create(0, this, &mThread)) {
     60    SANDBOX_LOG_ERRNO("SandboxReporter: thread creation failed");
     61    close(mClientFd);
     62    close(mServerFd);
     63    mClientFd = mServerFd = -1;
     64    return false;
     65  }
     66 
     67  return true;
     68 }
     69 
     70 SandboxReporter::~SandboxReporter() {
     71  if (mServerFd < 0) {
     72    return;
     73  }
     74  shutdown(mServerFd, SHUT_RD);
     75  PlatformThread::Join(mThread);
     76  close(mServerFd);
     77  close(mClientFd);
     78 }
     79 
     80 /* static */
     81 SandboxReporter* SandboxReporter::Singleton() {
     82  static StaticMutex sMutex MOZ_UNANNOTATED;
     83  StaticMutexAutoLock lock(sMutex);
     84 
     85  if (sSingleton == nullptr) {
     86    sSingleton = new SandboxReporter();
     87    if (!sSingleton->Init()) {
     88      // If socketpair or thread creation failed, trying to continue
     89      // with child process creation is unlikely to succeed; crash
     90      // instead of trying to handle that case.
     91      MOZ_CRASH("SandboxRepoter::Singleton: initialization failed");
     92    }
     93    // ClearOnShutdown must be called on the main thread and will
     94    // destroy the object on the main thread.  That *should* be safe;
     95    // the destructor will shut down the reporter's socket reader
     96    // thread before freeing anything, IPC should already be shut down
     97    // by that point (so it won't race by calling Singleton()), all
     98    // non-main XPCOM threads will also be shut down, and currently
     99    // the only other user is the main-thread-only Troubleshoot.sys.mjs.
    100    NS_DispatchToMainThread(NS_NewRunnableFunction(
    101        "SandboxReporter::Singleton", [] { ClearOnShutdown(&sSingleton); }));
    102  }
    103  return sSingleton.get();
    104 }
    105 
    106 int SandboxReporter::GetClientFileDescriptor() const {
    107  MOZ_ASSERT(mClientFd >= 0);
    108  return mClientFd;
    109 }
    110 
    111 // This function is mentioned in Histograms.json; keep that in mind if
    112 // it's renamed or moved to a different file.
    113 static void SubmitToTelemetry(const SandboxReport& aReport) {
    114  nsAutoCString key;
    115  // The key contains the process type, something that uniquely
    116  // identifies the syscall, and in some cases arguments (see below
    117  // for details).  Arbitrary formatting choice: fields in the key are
    118  // separated by ':', except that (arch, syscall#) pairs are
    119  // separated by '/'.
    120  //
    121  // Examples:
    122  // * "content:x86/64"           (bug 1285768)
    123  // * "content:x86_64/110"       (bug 1285768)
    124  // * "gmp:madvise:8"            (bug 1303813)
    125  // * "content:clock_gettime:4"  (bug 1334687)
    126 
    127  switch (aReport.mProcType) {
    128    case SandboxReport::ProcType::CONTENT:
    129      key.AppendLiteral("content");
    130      break;
    131    case SandboxReport::ProcType::FILE:
    132      key.AppendLiteral("file");
    133      break;
    134    case SandboxReport::ProcType::MEDIA_PLUGIN:
    135      key.AppendLiteral("gmp");
    136      break;
    137    case SandboxReport::ProcType::RDD:
    138      key.AppendLiteral("rdd");
    139      break;
    140    case SandboxReport::ProcType::SOCKET_PROCESS:
    141      key.AppendLiteral("socket");
    142      break;
    143    case SandboxReport::ProcType::UTILITY:
    144      key.AppendLiteral("utility");
    145      break;
    146    default:
    147      MOZ_ASSERT(false);
    148  }
    149  key.Append(':');
    150 
    151  switch (aReport.mSyscall) {
    152    // Syscalls that are filtered by arguments in one or more of the
    153    // policies in SandboxFilter.cpp should generally have those
    154    // arguments included here, but don't include irrelevant
    155    // information that would cause large numbers of distinct keys for
    156    // the same issue -- for example, pids or pointers.  When in
    157    // doubt, include arguments only if they would typically be
    158    // constants (or asm immediates) in the code making the syscall.
    159    //
    160    // Also, keep in mind that this is opt-out data collection and
    161    // privacy is critical.  While it's unlikely that information in
    162    // the register values alone could personally identify a user
    163    // (see also crash reports, where register contents are public),
    164    // and the guidelines in the previous paragraph should rule out
    165    // any value that's capable of holding PII, please be careful.
    166    //
    167    // When making changes here, please consult with a data steward
    168    // (https://wiki.mozilla.org/Firefox/Data_Collection) and ask for
    169    // a review if you are unsure about anything.
    170 
    171    // This macro includes one argument as a decimal number; it should
    172    // be enough for most cases.
    173 #define ARG_DECIMAL(name, idx)         \
    174  case __NR_##name:                    \
    175    key.AppendLiteral(#name ":");      \
    176    key.AppendInt(aReport.mArgs[idx]); \
    177    break
    178 
    179    // This may be more convenient if the argument is a set of bit flags.
    180 #define ARG_HEX(name, idx)                 \
    181  case __NR_##name:                        \
    182    key.AppendLiteral(#name ":0x");        \
    183    key.AppendInt(aReport.mArgs[idx], 16); \
    184    break
    185 
    186    // clockid_t is annoying: there are a small set of fixed timers,
    187    // but it can also encode a pid/tid (or a fd for a hardware clock
    188    // device); in this case the value is negative.
    189 #define ARG_CLOCKID(name, idx)                            \
    190  case __NR_##name:                                       \
    191    key.AppendLiteral(#name ":");                         \
    192    if (static_cast<clockid_t>(aReport.mArgs[idx]) < 0) { \
    193      key.AppendLiteral("dynamic");                       \
    194    } else {                                              \
    195      key.AppendInt(aReport.mArgs[idx]);                  \
    196    }                                                     \
    197    break
    198 
    199    // The syscalls handled specially:
    200 
    201    ARG_HEX(clone, 0);              // flags
    202    ARG_DECIMAL(prctl, 0);          // option
    203    ARG_HEX(ioctl, 1);              // request
    204    ARG_DECIMAL(fcntl, 1);          // cmd
    205    ARG_DECIMAL(madvise, 2);        // advice
    206    ARG_CLOCKID(clock_gettime, 0);  // clk_id
    207 
    208 #ifdef __NR_socketcall
    209    ARG_DECIMAL(socketcall, 0);  // call
    210 #endif
    211 #ifdef __NR_ipc
    212    ARG_DECIMAL(ipc, 0);  // call
    213 #endif
    214 
    215 #undef ARG_DECIMAL
    216 #undef ARG_HEX
    217 #undef ARG_CLOCKID
    218 
    219    default:
    220      // Otherwise just use the number, with the arch name to disambiguate.
    221      key.Append(SANDBOX_ARCH_NAME "/");
    222      key.AppendInt(aReport.mSyscall);
    223  }
    224 
    225  glean::sandbox::rejected_syscalls.Get(key).Add(1);
    226 }
    227 
    228 void SandboxReporter::AddOne(const SandboxReport& aReport) {
    229  SubmitToTelemetry(aReport);
    230 
    231  MutexAutoLock lock(mMutex);
    232  mBuffer[mCount % kSandboxReporterBufferSize] = aReport;
    233  ++mCount;
    234 }
    235 
    236 void SandboxReporter::ThreadMain(void) {
    237  // Create a nsThread wrapper for the current platform thread, and register it
    238  // with the thread manager.
    239  (void)NS_GetCurrentThread();
    240 
    241  PlatformThread::SetName("SandboxReporter");
    242  AUTO_PROFILER_REGISTER_THREAD("SandboxReporter");
    243 
    244  for (;;) {
    245    SandboxReport rep;
    246    struct iovec iov;
    247    struct msghdr msg;
    248 
    249    iov.iov_base = &rep;
    250    iov.iov_len = sizeof(rep);
    251    PodZero(&msg);
    252    msg.msg_iov = &iov;
    253    msg.msg_iovlen = 1;
    254 
    255    const auto recvd = recvmsg(mServerFd, &msg, 0);
    256    if (recvd < 0) {
    257      if (errno == EINTR) {
    258        continue;
    259      }
    260      SANDBOX_LOG_ERRNO("SandboxReporter: recvmsg");
    261    }
    262    if (recvd <= 0) {
    263      break;
    264    }
    265 
    266    if (static_cast<size_t>(recvd) < sizeof(rep)) {
    267      SANDBOX_LOG("SandboxReporter: packet too short (%d < %d)", recvd,
    268                  sizeof(rep));
    269      continue;
    270    }
    271    if (msg.msg_flags & MSG_TRUNC) {
    272      SANDBOX_LOG("SandboxReporter: packet too long");
    273      continue;
    274    }
    275 
    276    AddOne(rep);
    277  }
    278 }
    279 
    280 SandboxReporter::Snapshot SandboxReporter::GetSnapshot() {
    281  Snapshot snapshot;
    282  MutexAutoLock lock(mMutex);
    283 
    284  const uint64_t bufSize = static_cast<uint64_t>(kSandboxReporterBufferSize);
    285  const uint64_t start = std::max(mCount, bufSize) - bufSize;
    286  snapshot.mOffset = start;
    287  snapshot.mReports.Clear();
    288  snapshot.mReports.SetCapacity(mCount - start);
    289  for (size_t i = start; i < mCount; ++i) {
    290    const SandboxReport* rep = &mBuffer[i % kSandboxReporterBufferSize];
    291    MOZ_ASSERT(rep->IsValid());
    292    snapshot.mReports.AppendElement(*rep);
    293  }
    294  return snapshot;
    295 }
    296 
    297 }  // namespace mozilla