tor-browser

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

DecoderDoctorLogger.cpp (6507B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "DecoderDoctorLogger.h"
      8 
      9 #include "DDLogUtils.h"
     10 #include "DDMediaLogs.h"
     11 #include "mozilla/ClearOnShutdown.h"
     12 #include "mozilla/SchedulerGroup.h"
     13 #include "mozilla/StaticPtr.h"
     14 
     15 namespace mozilla {
     16 
     17 /* static */ Atomic<DecoderDoctorLogger::LogState, ReleaseAcquire>
     18    DecoderDoctorLogger::sLogState{DecoderDoctorLogger::scDisabled};
     19 
     20 /* static */ const char* DecoderDoctorLogger::sShutdownReason = nullptr;
     21 
     22 static DDMediaLogs* sMediaLogs;
     23 
     24 /* static */
     25 void DecoderDoctorLogger::Init() {
     26  MOZ_ASSERT(static_cast<LogState>(sLogState) == scDisabled);
     27  if (MOZ_LOG_TEST(sDecoderDoctorLoggerLog, LogLevel::Error) ||
     28      MOZ_LOG_TEST(sDecoderDoctorLoggerEndLog, LogLevel::Error)) {
     29    EnableLogging();
     30  }
     31 }
     32 
     33 #ifndef RELEASE_OR_BETA
     34 // First DDLogShutdowner sets sLogState to scShutdown, to prevent further
     35 // logging.
     36 struct DDLogShutdowner {
     37  ~DDLogShutdowner() {
     38    DDL_INFO("Shutting down");
     39    // Prevent further logging, some may racily seep in, it's fine as the
     40    // logging infrastructure would still be alive until DDLogDeleter runs.
     41    DecoderDoctorLogger::ShutdownLogging();
     42  }
     43 };
     44 static StaticAutoPtr<DDLogShutdowner> sDDLogShutdowner;
     45 
     46 // Later DDLogDeleter will delete the message queue and media logs.
     47 struct DDLogDeleter {
     48  ~DDLogDeleter() {
     49    if (sMediaLogs) {
     50      DDL_INFO("Final processing of collected logs");
     51      delete sMediaLogs;
     52      sMediaLogs = nullptr;
     53    }
     54  }
     55 };
     56 static StaticAutoPtr<DDLogDeleter> sDDLogDeleter;
     57 #endif
     58 
     59 /* static */
     60 void DecoderDoctorLogger::PanicInternal(const char* aReason, bool aDontBlock) {
     61  for (;;) {
     62    const LogState state = static_cast<LogState>(sLogState);
     63    if (state == scEnabling && !aDontBlock) {
     64      // Wait for the end of the enabling process (unless we're in it, in which
     65      // case we don't want to block.)
     66      continue;
     67    }
     68    if (state == scShutdown) {
     69      // Already shutdown, nothing more to do.
     70      break;
     71    }
     72    if (sLogState.compareExchange(state, scShutdown)) {
     73      // We are the one performing the first shutdown -> Record reason.
     74      sShutdownReason = aReason;
     75      // Free as much memory as possible.
     76      if (sMediaLogs) {
     77        // Shutdown the medialogs processing thread, and free as much memory
     78        // as possible.
     79        sMediaLogs->Panic();
     80      }
     81      // sMediaLogs and sQueue will be deleted by DDLogDeleter.
     82      // We don't want to delete them right now, because there could be a race
     83      // where another thread started logging or retrieving logs before we
     84      // changed the state to scShutdown, but has been delayed before actually
     85      // trying to write or read log messages, thereby causing a UAF.
     86    }
     87    // If someone else changed the state, we'll just loop around, and either
     88    // shutdown already happened elsewhere, or we'll try to shutdown again.
     89  }
     90 }
     91 
     92 /* static */
     93 bool DecoderDoctorLogger::EnsureLogIsEnabled() {
     94 #ifdef RELEASE_OR_BETA
     95  // Just refuse to enable DDLogger on release&beta because it makes it too easy
     96  // to trigger an OOM. See bug 1571648.
     97  return false;
     98 #else
     99  for (;;) {
    100    LogState state = static_cast<LogState>(sLogState);
    101    switch (state) {
    102      case scDisabled:
    103        // Currently disabled, try to be the one to enable.
    104        if (sLogState.compareExchange(scDisabled, scEnabling)) {
    105          // We are the one to enable logging, state won't change (except for
    106          // possible shutdown.)
    107          // Create DDMediaLogs singleton, which will process the message queue.
    108          DDMediaLogs::ConstructionResult mediaLogsConstruction =
    109              DDMediaLogs::New();
    110          if (NS_FAILED(mediaLogsConstruction.mRv)) {
    111            PanicInternal("Failed to enable logging", /* aDontBlock */ true);
    112            return false;
    113          }
    114          MOZ_ASSERT(mediaLogsConstruction.mMediaLogs);
    115          sMediaLogs = mediaLogsConstruction.mMediaLogs;
    116          // Setup shutdown-time clean-up.
    117          MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(
    118              NS_NewRunnableFunction("DDLogger shutdown setup", [] {
    119                sDDLogShutdowner = new DDLogShutdowner();
    120                ClearOnShutdown(&sDDLogShutdowner,
    121                                ShutdownPhase::XPCOMShutdown);
    122                sDDLogDeleter = new DDLogDeleter();
    123                ClearOnShutdown(&sDDLogDeleter,
    124                                ShutdownPhase::XPCOMShutdownThreads);
    125              })));
    126 
    127          // Nobody else should change the state when *we* are enabling logging.
    128          MOZ_ASSERT(sLogState == scEnabling);
    129          sLogState = scEnabled;
    130          DDL_INFO("Logging enabled");
    131          return true;
    132        }
    133        // Someone else changed the state before our compareExchange, just loop
    134        // around to examine the new situation.
    135        break;
    136      case scEnabled:
    137        return true;
    138      case scEnabling:
    139        // Someone else is currently enabling logging, actively wait by just
    140        // looping, until the state changes.
    141        break;
    142      case scShutdown:
    143        // Shutdown is non-recoverable, we cannot enable logging again.
    144        return false;
    145    }
    146    // Not returned yet, loop around to examine the new situation.
    147  }
    148 #endif
    149 }
    150 
    151 /* static */
    152 void DecoderDoctorLogger::EnableLogging() { (void)EnsureLogIsEnabled(); }
    153 
    154 /* static */ RefPtr<DecoderDoctorLogger::LogMessagesPromise>
    155 DecoderDoctorLogger::RetrieveMessages(
    156    const dom::HTMLMediaElement* aMediaElement) {
    157  if (MOZ_UNLIKELY(!EnsureLogIsEnabled())) {
    158    DDL_WARN("Request (for %p) but there are no logs", aMediaElement);
    159    return DecoderDoctorLogger::LogMessagesPromise::CreateAndReject(
    160        NS_ERROR_DOM_MEDIA_ABORT_ERR, __func__);
    161  }
    162  return sMediaLogs->RetrieveMessages(aMediaElement);
    163 }
    164 
    165 /* static */
    166 void DecoderDoctorLogger::Log(const char* aSubjectTypeName,
    167                              const void* aSubjectPointer,
    168                              DDLogCategory aCategory, const char* aLabel,
    169                              DDLogValue&& aValue) {
    170  if (IsDDLoggingEnabled()) {
    171    MOZ_ASSERT(sMediaLogs);
    172    sMediaLogs->Log(aSubjectTypeName, aSubjectPointer, aCategory, aLabel,
    173                    std::move(aValue));
    174  }
    175 }
    176 
    177 }  // namespace mozilla