tor-browser

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

SandboxProfiler.cpp (11218B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 #include <time.h>
      6 #include <unistd.h>
      7 #include <cstring>
      8 
      9 #include "SandboxInfo.h"
     10 
     11 #include "SandboxProfilerChild.h"
     12 #include "SandboxProfiler.h"
     13 
     14 #include "mozilla/Atomics.h"
     15 #include "mozilla/StaticPtr.h"
     16 #include "mozilla/PodOperations.h"
     17 
     18 namespace mozilla {
     19 
     20 #if defined(DEBUG)
     21 thread_local Atomic<bool> sInSignalContext = Atomic<bool>(false);
     22 
     23 AutoForbidSignalContext::AutoForbidSignalContext() { sInSignalContext = true; }
     24 
     25 AutoForbidSignalContext::~AutoForbidSignalContext() {
     26  sInSignalContext = false;
     27 }
     28 #endif  // defined(DEBUG)
     29 
     30 static StaticAutoPtr<SandboxProfiler> gProfiler;
     31 static StaticAutoPtr<SandboxProfilerQueue> gSyscallsQueue;
     32 static StaticAutoPtr<SandboxProfilerQueue> gLogsQueue;
     33 
     34 static Atomic<bool> isShutdown = Atomic<bool>(false);
     35 
     36 // Those function pointers are set by the main thread, and subsequently read by
     37 // all other thread, in particular in SIGSYS handlers.
     38 struct UprofilerFuncPtrs uprofiler;
     39 bool uprofiler_initted = false;
     40 
     41 static bool const SANDBOX_PROFILER_DEBUG = false;
     42 
     43 // Semaphores that we use to signal the SandboxProfilerEmitter thread when data
     44 // has been pushed to the SandboxProfilerQueue
     45 static sem_t gSyscallRequest;
     46 static sem_t gLogsRequest;
     47 
     48 // This is only be called on main thread, and not within SIGSYS context
     49 //
     50 // We might be called either from the profiler-started notification observer in
     51 // which case the !Active() call is not useful, but also directly from Sandbox'
     52 // SandboxLateInit where we want to verify if we are not already active: that
     53 // can happen if the user started the profiler via MOZ_PROFILER_STARTUP=1
     54 
     55 /* static */
     56 void SandboxProfiler::Create() {
     57  MOZ_ASSERT(!sInSignalContext,
     58             "SandboxProfiler::Create called in SIGSYS handler");
     59 
     60  if (!Init()) {
     61    return;
     62  }
     63 
     64  if (!Active()) {
     65    return;
     66  }
     67 
     68  if (!gSyscallsQueue) {
     69    gSyscallsQueue = new SandboxProfilerQueue();
     70  }
     71 
     72  if (!gLogsQueue) {
     73    gLogsQueue = new SandboxProfilerQueue();
     74  }
     75 
     76  if (!gProfiler) {
     77    gProfiler = new SandboxProfiler();
     78  }
     79 }
     80 
     81 SandboxProfiler::SandboxProfiler() {
     82  mThreadLogs = std::thread(&SandboxProfiler::ThreadMain, this,
     83                            "SandboxProfilerEmitterLogs", gLogsQueue.get(),
     84                            &gLogsRequest);
     85  mThreadSyscalls = std::thread(&SandboxProfiler::ThreadMain, this,
     86                                "SandboxProfilerEmitterSyscalls",
     87                                gSyscallsQueue.get(), &gSyscallRequest);
     88 }
     89 
     90 /* static */
     91 void SandboxProfiler::Shutdown() {
     92  isShutdown = true;
     93 
     94  if (gProfiler) {
     95    // Unblock remaining
     96    SandboxProfiler::Signal(&gSyscallRequest);
     97    SandboxProfiler::Signal(&gLogsRequest);
     98  }
     99 
    100  gProfiler = nullptr;
    101  gSyscallsQueue = nullptr;
    102  gLogsQueue = nullptr;
    103 }
    104 
    105 SandboxProfiler::~SandboxProfiler() {
    106  if (mThreadLogs.joinable()) {
    107    mThreadLogs.join();
    108  }
    109 
    110  if (mThreadSyscalls.joinable()) {
    111    mThreadSyscalls.join();
    112  }
    113 }
    114 
    115 /* static */
    116 bool SandboxProfiler::ActiveWithQueue(SandboxProfilerQueue* aQueue) {
    117  return !isShutdown && gProfiler && Active() && aQueue;
    118 }
    119 
    120 /* static */
    121 void SandboxProfiler::Signal(sem_t* aSem) {
    122  if (sem_post(aSem) < 0) {
    123    if constexpr (SANDBOX_PROFILER_DEBUG) {
    124      fprintf(stderr, "[%d] %s SEM_POST errno=%d\n", getpid(),
    125              __PRETTY_FUNCTION__, errno);
    126    }
    127  }
    128 }
    129 
    130 /* static */
    131 int SandboxProfiler::Wait(sem_t* aSem) { return sem_wait(aSem); }
    132 
    133 /* static */
    134 void SandboxProfiler::ReportInit(const void* top) {
    135  if (!ActiveWithQueue(gSyscallsQueue)) {
    136    return;
    137  }
    138 
    139  SandboxProfilerPayload payload = {
    140      .mStack = NativeStack{.mCount = 0},
    141      .mType = SandboxProfilerPayloadType::Init,
    142  };
    143  uprofiler.native_backtrace(top, &payload.mStack);
    144 
    145  MOZ_ASSERT(gSyscallsQueue, "Queue is valid for Send() from ReportInit()");
    146  if (!gSyscallsQueue) {
    147    if constexpr (SANDBOX_PROFILER_DEBUG) {
    148      fprintf(stderr,
    149              "[%d] WARNING: Hello PRODUCER: gSyscallsQueue disappeared\n",
    150              getpid());
    151    }
    152    return;
    153  }
    154 
    155  int rv = gSyscallsQueue->Send(payload);
    156  if (rv == 0) {
    157    if constexpr (SANDBOX_PROFILER_DEBUG) {
    158      fprintf(stderr,
    159              "[%d] WARNING: Hello PRODUCER: one stack mCount=%zu DROPPED\n",
    160              getpid(), payload.mStack.mCount);
    161    }
    162  }
    163 
    164  SandboxProfiler::Signal(&gSyscallRequest);
    165 }
    166 
    167 void SandboxProfiler::ReportInitImpl(SandboxProfilerPayload& payload,
    168                                     ProfileChunkedBuffer& buffer) {
    169  const char buf[] = "uprofiler init";
    170  std::array arg_names = {"init"};
    171  std::array arg_types = {
    172      TRACE_VALUE_TYPE_STRING,
    173  };
    174  std::array arg_values = {reinterpret_cast<unsigned long long>(buf)};
    175 
    176  Report("SandboxBroker::InitWithStack", arg_names, arg_types, arg_values,
    177         &buffer);
    178 }
    179 
    180 /* static */
    181 void SandboxProfiler::ReportLog(const char* aBuf) {
    182  if (!ActiveWithQueue(gLogsQueue)) {
    183    return;
    184  }
    185 
    186  if (!SandboxInfo::Get().Test(SandboxInfo::kVerbose) &&
    187      !SandboxInfo::Get().Test(SandboxInfo::kVerboseTests)) {
    188    return;
    189  }
    190 
    191  SandboxProfilerPayload payload = {
    192      .mStack = NativeStack{.mCount = 0},
    193      .mType = SandboxProfilerPayloadType::Log,
    194  };
    195 
    196  const size_t bufLen = strnlen(aBuf, PATH_MAX);
    197  PodCopy(payload.mPath, aBuf, bufLen);
    198 
    199  MOZ_ASSERT(gLogsQueue, "Queue is valid for Send() from ReportLog()");
    200  if (!gLogsQueue) {
    201    if constexpr (SANDBOX_PROFILER_DEBUG) {
    202      fprintf(stderr, "[%d] WARNING: Hello PRODUCER: gLogsQueue disappeared\n",
    203              getpid());
    204    }
    205    return;
    206  }
    207 
    208  int rv = gLogsQueue->Send(payload);
    209  if (rv == 0) {
    210    if constexpr (SANDBOX_PROFILER_DEBUG) {
    211      fprintf(stderr, "[%d] WARNING: Hello PRODUCER: one log stack DROPPED\n",
    212              getpid());
    213    }
    214  }
    215 
    216  SandboxProfiler::Signal(&gLogsRequest);
    217 }
    218 
    219 void SandboxProfiler::ReportLogImpl(SandboxProfilerPayload& payload) {
    220  std::array arg_names = {"log"};
    221  std::array arg_types = {
    222      TRACE_VALUE_TYPE_STRING,
    223  };
    224  std::array arg_values = {
    225      reinterpret_cast<unsigned long long>(payload.mPath),
    226  };
    227 
    228  Report("SandboxBroker::Log", arg_names, arg_types, arg_values, nullptr);
    229 }
    230 
    231 /* static */
    232 void SandboxProfiler::ReportRequest(const void* top, uint64_t aId,
    233                                    const char* aOp, int aFlags,
    234                                    const char* aPath, const char* aPath2,
    235                                    pid_t aPid) {
    236  if (!ActiveWithQueue(gSyscallsQueue)) {
    237    return;
    238  }
    239 
    240  // Take a stack, this should be safe to do in the context of SIGSYS
    241  SandboxProfilerPayload payload = {
    242      .mStack = NativeStack{.mCount = 0},
    243      .mId = aId,
    244      .mOp = aOp,
    245      .mFlags = aFlags,
    246      .mPid = aPid,
    247      .mType = SandboxProfilerPayloadType::Request,
    248  };
    249 
    250  if (aPath) {
    251    const size_t pathLen = strnlen(aPath, PATH_MAX);
    252    PodCopy(payload.mPath, aPath, pathLen);
    253  } else {
    254    payload.mPath[0] = '\0';
    255  }
    256 
    257  if (aPath2) {
    258    const size_t path2Len = strnlen(aPath2, PATH_MAX);
    259    PodCopy(payload.mPath2, aPath2, path2Len);
    260  } else {
    261    payload.mPath2[0] = '\0';
    262  }
    263 
    264  uprofiler.native_backtrace(top, &payload.mStack);
    265 
    266  MOZ_ASSERT(gSyscallsQueue, "Queue is valid for Send() from ReportRequest()");
    267  if (!gSyscallsQueue) {
    268    if constexpr (SANDBOX_PROFILER_DEBUG) {
    269      fprintf(stderr,
    270              "[%d] WARNING: Hello PRODUCER: gSyscallsQueue disappeared\n",
    271              getpid());
    272    }
    273    return;
    274  }
    275 
    276  int rv = gSyscallsQueue->Send(payload);
    277  if (rv == 0) {
    278    if constexpr (SANDBOX_PROFILER_DEBUG) {
    279      fprintf(stderr,
    280              "[%d] WARNING: Hello PRODUCER: one stack mCount=%zu DROPPED\n",
    281              getpid(), payload.mStack.mCount);
    282    }
    283  }
    284 
    285  SandboxProfiler::Signal(&gSyscallRequest);
    286 }
    287 
    288 void SandboxProfiler::ReportRequestImpl(SandboxProfilerPayload& payload,
    289                                        ProfileChunkedBuffer& buffer) {
    290  std::array arg_names = {"id", "op", "rflags", "path", "path2", "pid"};
    291  std::array arg_types = {
    292      TRACE_VALUE_TYPE_UINT,    // id
    293      TRACE_VALUE_TYPE_STRING,  // op
    294      TRACE_VALUE_TYPE_UINT,    // rflags
    295      TRACE_VALUE_TYPE_STRING,  // path
    296      TRACE_VALUE_TYPE_STRING,  // path2
    297      TRACE_VALUE_TYPE_UINT     // pid
    298  };
    299 
    300  std::array arg_values = {static_cast<unsigned long long>(payload.mId),
    301                           reinterpret_cast<unsigned long long>(payload.mOp),
    302                           static_cast<unsigned long long>(payload.mFlags),
    303                           reinterpret_cast<unsigned long long>(payload.mPath),
    304                           reinterpret_cast<unsigned long long>(payload.mPath2),
    305                           static_cast<unsigned long long>(payload.mPid)};
    306 
    307  Report("SandboxBrokerClient", arg_names, arg_types, arg_values, &buffer);
    308 }
    309 
    310 void SandboxProfiler::ThreadMain(const char* aThreadName,
    311                                 SandboxProfilerQueue* aQueue,
    312                                 sem_t* aRequest) {
    313  uprofiler.register_thread(aThreadName, CallerPC());
    314  SandboxProfilerPayload p;
    315 
    316  DebugOnly<int> sem_init_rv =
    317      sem_init(aRequest, /* pshared */ 0, /* value */ 0);
    318  MOZ_ASSERT(sem_init_rv == 0, "Failure to initialize semaphore");
    319 
    320  while (!isShutdown) {
    321    errno = 0;
    322    if (SandboxProfiler::Wait(aRequest) < 0) {
    323      int _errno = errno;
    324      MOZ_ASSERT(_errno != EINVAL, "sem_wait() returned EINVAL");
    325      if (_errno == EAGAIN || _errno == EINTR) {
    326        continue;
    327      }
    328    }
    329 
    330    MOZ_ASSERT(aQueue, "Syscalls queue is valid for Recv()");
    331    if (!aQueue) {
    332      if constexpr (SANDBOX_PROFILER_DEBUG) {
    333        fprintf(stderr,
    334                "[%d] WARNING: Hello CONSUMER [%s]: aQueue disappeared\n",
    335                getpid(), aThreadName);
    336      }
    337      continue;
    338    }
    339 
    340    int deq = aQueue->Recv(&p);
    341    if (deq > 0) {
    342      switch (p.mType) {
    343        case SandboxProfilerPayloadType::Init:
    344        case SandboxProfilerPayloadType::Request: {
    345          ProfileBufferChunkManagerSingle chunkManager{
    346              mozilla::ProfileBufferChunkManager::scExpectedMaximumStackSize};
    347          ProfileChunkedBuffer chunkedBuffer{
    348              ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager};
    349          uprofiler.backtrace_into_buffer(&p.mStack, &chunkedBuffer);
    350 
    351          switch (p.mType) {
    352            case SandboxProfilerPayloadType::Init:
    353              ReportInitImpl(p, chunkedBuffer);
    354              break;
    355 
    356            case SandboxProfilerPayloadType::Request:
    357              ReportRequestImpl(p, chunkedBuffer);
    358              break;
    359 
    360            default:
    361              // impossible?
    362              MOZ_ASSERT_UNREACHABLE("Should have been Init/Request");
    363              break;
    364          }
    365        } break;
    366 
    367        case SandboxProfilerPayloadType::Log:
    368          ReportLogImpl(p);
    369          break;
    370 
    371        default:
    372          fprintf(stderr, "[%d] mType=%hhu\n", getpid(),
    373                  static_cast<uint8_t>(p.mType));
    374          MOZ_CRASH("Unsupported type");
    375          break;
    376      }
    377    }
    378  }
    379 
    380  sem_destroy(aRequest);
    381 
    382  uprofiler.unregister_thread();
    383 }
    384 
    385 }  // namespace mozilla