tor-browser

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

ProcessRuntime.cpp (18025B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/mscom/ProcessRuntime.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "mozilla/DynamicallyLinkedFunctionPtr.h"
     11 #include "mozilla/mscom/COMWrappers.h"
     12 #include "mozilla/mscom/ProcessRuntimeShared.h"
     13 #include "mozilla/RefPtr.h"
     14 #include "mozilla/UniquePtr.h"
     15 #include "mozilla/Vector.h"
     16 #include "mozilla/WindowsProcessMitigations.h"
     17 
     18 #if defined(MOZILLA_INTERNAL_API)
     19 #  include "mozilla/mscom/EnsureMTA.h"
     20 #  if defined(MOZ_SANDBOX)
     21 #    include "mozilla/sandboxTarget.h"
     22 #  endif  // defined(MOZ_SANDBOX)
     23 #endif    // defined(MOZILLA_INTERNAL_API)
     24 
     25 #include <accctrl.h>
     26 #include <aclapi.h>
     27 #include <objbase.h>
     28 #include <objidl.h>
     29 
     30 // This API from oleaut32.dll is not declared in Windows SDK headers
     31 extern "C" void __cdecl SetOaNoCache(void);
     32 
     33 using namespace mozilla::mscom::detail;
     34 
     35 namespace mozilla {
     36 namespace mscom {
     37 
     38 #if defined(MOZILLA_INTERNAL_API)
     39 ProcessRuntime* ProcessRuntime::sInstance = nullptr;
     40 
     41 ProcessRuntime::ProcessRuntime() : ProcessRuntime(XRE_GetProcessType()) {}
     42 
     43 ProcessRuntime::ProcessRuntime(const GeckoProcessType aProcessType)
     44    : ProcessRuntime(aProcessType == GeckoProcessType_Default
     45                         ? ProcessCategory::GeckoBrowserParent
     46                         : ProcessCategory::GeckoChild) {}
     47 #endif  // defined(MOZILLA_INTERNAL_API)
     48 
     49 ProcessRuntime::ProcessRuntime(const ProcessCategory aProcessCategory)
     50    : mInitResult(CO_E_NOTINITIALIZED), mProcessCategory(aProcessCategory) {
     51 #if defined(MOZILLA_INTERNAL_API)
     52  MOZ_DIAGNOSTIC_ASSERT(!sInstance);
     53  sInstance = this;
     54 
     55  EnsureMTA();
     56  /**
     57   * From this point forward, all threads in this process are implicitly
     58   * members of the multi-threaded apartment, with the following exceptions:
     59   * 1. If any Win32 GUI APIs were called on the current thread prior to
     60   *    executing this constructor, then this thread has already been implicitly
     61   *    initialized as the process's main STA thread; or
     62   * 2. A thread explicitly and successfully calls CoInitialize(Ex) to specify
     63   *    otherwise.
     64   */
     65 
     66  const bool isCurThreadImplicitMTA = IsCurrentThreadImplicitMTA();
     67  // We only assert that the implicit MTA precondition holds when not running
     68  // as the Gecko parent process.
     69  MOZ_DIAGNOSTIC_ASSERT(aProcessCategory ==
     70                            ProcessCategory::GeckoBrowserParent ||
     71                        isCurThreadImplicitMTA);
     72 
     73 #  if defined(MOZ_SANDBOX)
     74  const bool isLockedDownChildProcess =
     75      mProcessCategory == ProcessCategory::GeckoChild && IsWin32kLockedDown();
     76  // If our process is running under Win32k lockdown, we cannot initialize
     77  // COM with a single-threaded apartment. This is because STAs create a hidden
     78  // window, which implicitly requires user32 and Win32k, which are blocked.
     79  // Instead we start the multi-threaded apartment and conduct our process-wide
     80  // COM initialization there.
     81  if (isLockedDownChildProcess) {
     82    // Make sure we're still running with the sandbox's privileged impersonation
     83    // token.
     84    HANDLE rawCurThreadImpToken;
     85    if (!::OpenThreadToken(::GetCurrentThread(), TOKEN_DUPLICATE | TOKEN_QUERY,
     86                           FALSE, &rawCurThreadImpToken)) {
     87      mInitResult = HRESULT_FROM_WIN32(::GetLastError());
     88      return;
     89    }
     90    nsAutoHandle curThreadImpToken(rawCurThreadImpToken);
     91 
     92    // Ensure that our current token is still an impersonation token (ie, we
     93    // have not yet called RevertToSelf() on this thread).
     94    DWORD len;
     95    TOKEN_TYPE tokenType;
     96    MOZ_RELEASE_ASSERT(
     97        ::GetTokenInformation(rawCurThreadImpToken, TokenType, &tokenType,
     98                              sizeof(tokenType), &len) &&
     99        len == sizeof(tokenType) && tokenType == TokenImpersonation);
    100 
    101    // Ideally we want our current thread to be running implicitly inside the
    102    // MTA, but if for some wacky reason we did not end up with that, we may
    103    // compensate by completing initialization via EnsureMTA's persistent
    104    // thread.
    105    if (!isCurThreadImplicitMTA) {
    106      InitUsingPersistentMTAThread(curThreadImpToken);
    107      return;
    108    }
    109  }
    110 #  endif  // defined(MOZ_SANDBOX)
    111 #endif    // defined(MOZILLA_INTERNAL_API)
    112 
    113  mAptRegion.Init(GetDesiredApartmentType(mProcessCategory));
    114 
    115  // It can happen that we are not the outermost COM initialization on this
    116  // thread. In fact it should regularly be the case that the outermost
    117  // initialization occurs from outside of XUL, before we show the skeleton UI,
    118  // at which point we still need to run some things here from within XUL.
    119  if (!mAptRegion.IsValidOutermost()) {
    120    mInitResult = mAptRegion.GetHResult();
    121 #if defined(MOZILLA_INTERNAL_API)
    122    MOZ_ASSERT(mProcessCategory == ProcessCategory::GeckoBrowserParent);
    123    if (mProcessCategory != ProcessCategory::GeckoBrowserParent) {
    124      // This is unexpected unless we're GeckoBrowserParent
    125      return;
    126    }
    127 
    128    ProcessInitLock lock;
    129 
    130    // Is another instance of ProcessRuntime responsible for the outer
    131    // initialization?
    132    const bool prevInit =
    133        lock.GetInitState() == ProcessInitState::FullyInitialized;
    134    MOZ_ASSERT(prevInit);
    135    if (prevInit) {
    136      PostInit();
    137    }
    138 #endif  // defined(MOZILLA_INTERNAL_API)
    139    return;
    140  }
    141 
    142  InitInsideApartment();
    143  if (FAILED(mInitResult)) {
    144    return;
    145  }
    146 
    147 #if defined(MOZILLA_INTERNAL_API)
    148 #  if defined(MOZ_SANDBOX)
    149  if (isLockedDownChildProcess) {
    150    // In locked-down child processes, defer PostInit until priv drop
    151    SandboxTarget::Instance()->RegisterSandboxStartCallback([self = this]() {
    152      // Ensure that we're still live and the init was successful before
    153      // calling PostInit()
    154      if (self == sInstance && SUCCEEDED(self->mInitResult)) {
    155        PostInit();
    156      }
    157    });
    158    return;
    159  }
    160 #  endif  // defined(MOZ_SANDBOX)
    161 
    162  PostInit();
    163 #endif  // defined(MOZILLA_INTERNAL_API)
    164 }
    165 
    166 #if defined(MOZILLA_INTERNAL_API)
    167 ProcessRuntime::~ProcessRuntime() {
    168  MOZ_DIAGNOSTIC_ASSERT(sInstance == this);
    169  sInstance = nullptr;
    170 }
    171 
    172 #  if defined(MOZ_SANDBOX)
    173 void ProcessRuntime::InitUsingPersistentMTAThread(
    174    const nsAutoHandle& aCurThreadToken) {
    175  // Create an impersonation token based on the current thread's token
    176  HANDLE rawMtaThreadImpToken = nullptr;
    177  if (!::DuplicateToken(aCurThreadToken, SecurityImpersonation,
    178                        &rawMtaThreadImpToken)) {
    179    mInitResult = HRESULT_FROM_WIN32(::GetLastError());
    180    return;
    181  }
    182  nsAutoHandle mtaThreadImpToken(rawMtaThreadImpToken);
    183 
    184  // Impersonate and initialize.
    185  bool tokenSet = false;
    186  EnsureMTA(
    187      [this, rawMtaThreadImpToken, &tokenSet]() -> void {
    188        if (!::SetThreadToken(nullptr, rawMtaThreadImpToken)) {
    189          mInitResult = HRESULT_FROM_WIN32(::GetLastError());
    190          return;
    191        }
    192 
    193        tokenSet = true;
    194        InitInsideApartment();
    195      },
    196      EnsureMTA::Option::ForceDispatchToPersistentThread);
    197 
    198  if (!tokenSet) {
    199    return;
    200  }
    201 
    202  SandboxTarget::Instance()->RegisterSandboxStartCallback(
    203      [self = this]() -> void {
    204        EnsureMTA(
    205            []() -> void {
    206              // This is a security risk if it fails, so we release assert
    207              MOZ_RELEASE_ASSERT(::RevertToSelf(),
    208                                 "mscom::ProcessRuntime RevertToSelf failed");
    209            },
    210            EnsureMTA::Option::ForceDispatchToPersistentThread);
    211 
    212        // Ensure that we're still live and the init was successful before
    213        // calling PostInit()
    214        if (self == sInstance && SUCCEEDED(self->mInitResult)) {
    215          PostInit();
    216        }
    217      });
    218 }
    219 #  endif  // defined(MOZ_SANDBOX)
    220 #endif    // defined(MOZILLA_INTERNAL_API)
    221 
    222 /* static */
    223 COINIT ProcessRuntime::GetDesiredApartmentType(
    224    const ProcessRuntime::ProcessCategory aProcessCategory) {
    225  switch (aProcessCategory) {
    226    case ProcessCategory::GeckoBrowserParent:
    227      return COINIT_APARTMENTTHREADED;
    228    case ProcessCategory::GeckoChild:
    229      if (!IsWin32kLockedDown()) {
    230        // If Win32k is not locked down then we probably still need STA.
    231        // We disable DDE since that is not usable from child processes.
    232        return static_cast<COINIT>(COINIT_APARTMENTTHREADED |
    233                                   COINIT_DISABLE_OLE1DDE);
    234      }
    235 
    236      [[fallthrough]];
    237    default:
    238      return COINIT_MULTITHREADED;
    239  }
    240 }
    241 
    242 void ProcessRuntime::InitInsideApartment() {
    243  ProcessInitLock lock;
    244  const ProcessInitState prevInitState = lock.GetInitState();
    245  if (prevInitState == ProcessInitState::FullyInitialized) {
    246    // COM has already been initialized by a previous ProcessRuntime instance
    247    mInitResult = S_OK;
    248    return;
    249  }
    250 
    251  if (prevInitState < ProcessInitState::PartialSecurityInitialized) {
    252    // We are required to initialize security prior to configuring global
    253    // options.
    254    mInitResult = InitializeSecurity(mProcessCategory);
    255    // Downgrading from a MOZ_DIAGNOSTIC_ASSERT while investigating
    256    // bug 1930846.
    257    MOZ_ASSERT(SUCCEEDED(mInitResult));
    258 
    259    // Even though this isn't great, we should try to proceed even when
    260    // CoInitializeSecurity has previously been called: the additional settings
    261    // we want to change are important enough that we don't want to skip them.
    262    if (FAILED(mInitResult) && mInitResult != RPC_E_TOO_LATE) {
    263      return;
    264    }
    265 
    266    lock.SetInitState(ProcessInitState::PartialSecurityInitialized);
    267  }
    268 
    269  if (prevInitState < ProcessInitState::PartialGlobalOptions) {
    270    RefPtr<IGlobalOptions> globalOpts;
    271    mInitResult = wrapped::CoCreateInstance(
    272        CLSID_GlobalOptions, nullptr, CLSCTX_INPROC_SERVER, IID_IGlobalOptions,
    273        getter_AddRefs(globalOpts));
    274    MOZ_ASSERT(SUCCEEDED(mInitResult));
    275    if (FAILED(mInitResult)) {
    276      return;
    277    }
    278 
    279    // Disable COM's catch-all exception handler
    280    mInitResult = globalOpts->Set(COMGLB_EXCEPTION_HANDLING,
    281                                  COMGLB_EXCEPTION_DONOT_HANDLE_ANY);
    282    MOZ_ASSERT(SUCCEEDED(mInitResult));
    283    if (FAILED(mInitResult)) {
    284      return;
    285    }
    286 
    287    lock.SetInitState(ProcessInitState::PartialGlobalOptions);
    288  }
    289 
    290  // Disable the BSTR cache (as it never invalidates, thus leaking memory)
    291  // (This function is itself idempotent, so we do not concern ourselves with
    292  // tracking whether or not we've already called it.)
    293  ::SetOaNoCache();
    294 
    295  lock.SetInitState(ProcessInitState::FullyInitialized);
    296 }
    297 
    298 #if defined(MOZILLA_INTERNAL_API)
    299 /**
    300 * Guaranteed to run *after* the COM (and possible sandboxing) initialization
    301 * has successfully completed and stabilized. This method MUST BE IDEMPOTENT!
    302 */
    303 /* static */ void ProcessRuntime::PostInit() {
    304  // Currently "roughed-in" but unused.
    305 }
    306 #endif  // defined(MOZILLA_INTERNAL_API)
    307 
    308 /* static */
    309 DWORD
    310 ProcessRuntime::GetClientThreadId() {
    311  DWORD callerTid;
    312  HRESULT hr = ::CoGetCallerTID(&callerTid);
    313  // Don't return callerTid unless the call succeeded and returned S_FALSE,
    314  // indicating that the caller originates from a different process.
    315  if (hr != S_FALSE) {
    316    return 0;
    317  }
    318 
    319  return callerTid;
    320 }
    321 
    322 /* static */
    323 HRESULT
    324 ProcessRuntime::InitializeSecurity(const ProcessCategory aProcessCategory) {
    325  HANDLE rawToken = nullptr;
    326  BOOL ok = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken);
    327  if (!ok) {
    328    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
    329    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    330    return hr;
    331  }
    332  nsAutoHandle token(rawToken);
    333 
    334  DWORD len = 0;
    335  ok = ::GetTokenInformation(token, TokenUser, nullptr, len, &len);
    336  DWORD win32Error = ::GetLastError();
    337  if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
    338    HRESULT hr = HRESULT_FROM_WIN32(win32Error);
    339    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    340    return hr;
    341  }
    342 
    343  auto tokenUserBuf = MakeUnique<BYTE[]>(len);
    344  TOKEN_USER& tokenUser = *reinterpret_cast<TOKEN_USER*>(tokenUserBuf.get());
    345  ok = ::GetTokenInformation(token, TokenUser, tokenUserBuf.get(), len, &len);
    346  if (!ok) {
    347    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
    348    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    349    return hr;
    350  }
    351 
    352  len = 0;
    353  ok = ::GetTokenInformation(token, TokenPrimaryGroup, nullptr, len, &len);
    354  win32Error = ::GetLastError();
    355  if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
    356    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
    357    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    358    return hr;
    359  }
    360 
    361  auto tokenPrimaryGroupBuf = MakeUnique<BYTE[]>(len);
    362  TOKEN_PRIMARY_GROUP& tokenPrimaryGroup =
    363      *reinterpret_cast<TOKEN_PRIMARY_GROUP*>(tokenPrimaryGroupBuf.get());
    364  ok = ::GetTokenInformation(token, TokenPrimaryGroup,
    365                             tokenPrimaryGroupBuf.get(), len, &len);
    366  if (!ok) {
    367    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
    368    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    369    return hr;
    370  }
    371 
    372  SECURITY_DESCRIPTOR sd;
    373  if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
    374    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
    375    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    376    return hr;
    377  }
    378 
    379  BYTE systemSid[SECURITY_MAX_SID_SIZE];
    380  DWORD systemSidSize = sizeof(systemSid);
    381  if (!::CreateWellKnownSid(WinLocalSystemSid, nullptr, systemSid,
    382                            &systemSidSize)) {
    383    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
    384    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    385    return hr;
    386  }
    387 
    388  BYTE adminSid[SECURITY_MAX_SID_SIZE];
    389  DWORD adminSidSize = sizeof(adminSid);
    390  if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, nullptr, adminSid,
    391                            &adminSidSize)) {
    392    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
    393    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    394    return hr;
    395  }
    396 
    397  const bool allowAllNonRestrictedAppContainers =
    398      aProcessCategory == ProcessCategory::GeckoBrowserParent;
    399 
    400  BYTE appContainersSid[SECURITY_MAX_SID_SIZE];
    401  DWORD appContainersSidSize = sizeof(appContainersSid);
    402  if (allowAllNonRestrictedAppContainers) {
    403    if (!::CreateWellKnownSid(WinBuiltinAnyPackageSid, nullptr,
    404                              appContainersSid, &appContainersSidSize)) {
    405      HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
    406      MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    407      return hr;
    408    }
    409  }
    410 
    411  UniquePtr<BYTE[]> tokenAppContainerInfBuf;
    412  len = 0;
    413  ::GetTokenInformation(token, TokenAppContainerSid, nullptr, len, &len);
    414  if (len) {
    415    tokenAppContainerInfBuf = MakeUnique<BYTE[]>(len);
    416    ok = ::GetTokenInformation(token, TokenAppContainerSid,
    417                               tokenAppContainerInfBuf.get(), len, &len);
    418    if (!ok) {
    419      // Don't fail if we get an error retrieving an app container SID.
    420      tokenAppContainerInfBuf = nullptr;
    421    }
    422  }
    423 
    424  // Grant access to SYSTEM, Administrators, the user, our app container (if in
    425  // one) and when running as the browser process on Windows 8+, all non
    426  // restricted app containers.
    427  const size_t kMaxInlineEntries = 5;
    428  mozilla::Vector<EXPLICIT_ACCESS_W, kMaxInlineEntries> entries;
    429 
    430  (void)entries.append(EXPLICIT_ACCESS_W{
    431      COM_RIGHTS_EXECUTE,
    432      GRANT_ACCESS,
    433      NO_INHERITANCE,
    434      {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
    435       reinterpret_cast<LPWSTR>(systemSid)}});
    436 
    437  (void)entries.append(EXPLICIT_ACCESS_W{
    438      COM_RIGHTS_EXECUTE,
    439      GRANT_ACCESS,
    440      NO_INHERITANCE,
    441      {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID,
    442       TRUSTEE_IS_WELL_KNOWN_GROUP, reinterpret_cast<LPWSTR>(adminSid)}});
    443 
    444  (void)entries.append(EXPLICIT_ACCESS_W{
    445      COM_RIGHTS_EXECUTE,
    446      GRANT_ACCESS,
    447      NO_INHERITANCE,
    448      {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
    449       reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}});
    450 
    451  if (allowAllNonRestrictedAppContainers) {
    452    (void)entries.append(
    453        EXPLICIT_ACCESS_W{COM_RIGHTS_EXECUTE,
    454                          GRANT_ACCESS,
    455                          NO_INHERITANCE,
    456                          {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID,
    457                           TRUSTEE_IS_WELL_KNOWN_GROUP,
    458                           reinterpret_cast<LPWSTR>(appContainersSid)}});
    459  }
    460 
    461  if (tokenAppContainerInfBuf) {
    462    TOKEN_APPCONTAINER_INFORMATION& tokenAppContainerInf =
    463        *reinterpret_cast<TOKEN_APPCONTAINER_INFORMATION*>(
    464            tokenAppContainerInfBuf.get());
    465 
    466    // TokenAppContainer will be null if we are not in an app container.
    467    if (tokenAppContainerInf.TokenAppContainer) {
    468      (void)entries.append(EXPLICIT_ACCESS_W{
    469          COM_RIGHTS_EXECUTE,
    470          GRANT_ACCESS,
    471          NO_INHERITANCE,
    472          {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
    473           reinterpret_cast<LPWSTR>(tokenAppContainerInf.TokenAppContainer)}});
    474    }
    475  }
    476 
    477  PACL rawDacl = nullptr;
    478  win32Error =
    479      ::SetEntriesInAclW(entries.length(), entries.begin(), nullptr, &rawDacl);
    480  if (win32Error != ERROR_SUCCESS) {
    481    HRESULT hr = HRESULT_FROM_WIN32(win32Error);
    482    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    483    return hr;
    484  }
    485 
    486  UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl);
    487 
    488  if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) {
    489    HRESULT hr = HRESULT_FROM_WIN32(win32Error);
    490    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    491    return hr;
    492  }
    493 
    494  if (!::SetSecurityDescriptorOwner(&sd, tokenUser.User.Sid, FALSE)) {
    495    HRESULT hr = HRESULT_FROM_WIN32(win32Error);
    496    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    497    return hr;
    498  }
    499 
    500  if (!::SetSecurityDescriptorGroup(&sd, tokenPrimaryGroup.PrimaryGroup,
    501                                    FALSE)) {
    502    HRESULT hr = HRESULT_FROM_WIN32(win32Error);
    503    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    504    return hr;
    505  }
    506 
    507  HRESULT hr = wrapped::CoInitializeSecurity(
    508      &sd, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT,
    509      RPC_C_IMP_LEVEL_IDENTIFY, nullptr, EOAC_NONE, nullptr);
    510  MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
    511  return hr;
    512 }
    513 
    514 }  // namespace mscom
    515 }  // namespace mozilla