tor-browser

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

SandboxTest.cpp (13822B)


      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 https://mozilla.org/MPL/2.0/. */
      6 
      7 #include "SandboxTest.h"
      8 
      9 #include "mozilla/Components.h"
     10 #include "mozilla/Preferences.h"
     11 #include "SandboxTestingParent.h"
     12 #include "SandboxTestingChild.h"
     13 #include "mozilla/dom/ContentParent.h"
     14 #include "mozilla/gfx/GPUProcessManager.h"
     15 #include "mozilla/gfx/GPUChild.h"
     16 #include "mozilla/net/SocketProcessParent.h"
     17 #include "mozilla/RDDProcessManager.h"
     18 #include "mozilla/RDDChild.h"
     19 #include "mozilla/ipc/UtilityProcessManager.h"
     20 #include "mozilla/ipc/UtilityProcessParent.h"
     21 #include "mozilla/ipc/UtilityProcessSandboxing.h"
     22 #include "GMPService.h"
     23 #include "mozilla/gmp/GMPTypes.h"
     24 #include "mozilla/ipc/Endpoint.h"
     25 #include "nsIOService.h"
     26 
     27 #ifdef XP_WIN
     28 #  include "nsAppDirectoryServiceDefs.h"
     29 #endif
     30 
     31 using namespace mozilla;
     32 using namespace mozilla::ipc;
     33 using namespace mozilla::dom;
     34 
     35 namespace mozilla {
     36 
     37 NS_IMPL_ISUPPORTS(SandboxTest, mozISandboxTest)
     38 
     39 inline void UnsetEnvVariable(const nsCString& aEnvVarName) {
     40  nsCString aEnvVarNameFull = aEnvVarName + "="_ns;
     41  int rv_unset =
     42 #ifdef XP_UNIX
     43      unsetenv(aEnvVarName.get());
     44 #endif  // XP_UNIX
     45 #ifdef XP_WIN
     46  _putenv(aEnvVarNameFull.get());
     47 #endif  // XP_WIN
     48  MOZ_ASSERT(rv_unset == 0, "Error unsetting env var");
     49 }
     50 
     51 GeckoProcessType GeckoProcessStringToType(const nsCString& aString) {
     52  for (GeckoProcessType type = GeckoProcessType(0);
     53       type < GeckoProcessType::GeckoProcessType_End;
     54       type = GeckoProcessType(type + 1)) {
     55    if (aString == XRE_GeckoProcessTypeToString(type)) {
     56      return type;
     57    }
     58  }
     59  return GeckoProcessType::GeckoProcessType_Invalid;
     60 }
     61 
     62 // Set up tests on remote process connected to the given actor.
     63 // The actor must handle the InitSandboxTesting message.
     64 template <typename Actor>
     65 void InitializeSandboxTestingActors(
     66    Actor* aActor,
     67    const RefPtr<SandboxTest::ProcessPromise::Private>& aProcessPromise) {
     68  MOZ_ASSERT(aActor, "Should have provided an IPC actor");
     69  Endpoint<PSandboxTestingParent> sandboxTestingParentEnd;
     70  Endpoint<PSandboxTestingChild> sandboxTestingChildEnd;
     71  nsresult rv = PSandboxTesting::CreateEndpoints(&sandboxTestingParentEnd,
     72                                                 &sandboxTestingChildEnd);
     73  if (NS_FAILED(rv)) {
     74    aProcessPromise->Reject(NS_ERROR_FAILURE, __func__);
     75    return;
     76  }
     77 
     78  // GMPlugin binds us to the GMP Thread, so we need IPC's Send to be done on
     79  // the same thread
     80  (void)aActor->SendInitSandboxTesting(std::move(sandboxTestingChildEnd));
     81  // But then the SandboxTestingParent::Create() call needs to be on the main
     82  // thread
     83  NS_DispatchToMainThread(NS_NewRunnableFunction(
     84      "SandboxTestingParent::Create",
     85      [stpE = std::move(sandboxTestingParentEnd), aProcessPromise]() mutable {
     86        return aProcessPromise->Resolve(
     87            SandboxTestingParent::Create(std::move(stpE)), __func__);
     88      }));
     89 }
     90 
     91 NS_IMETHODIMP
     92 SandboxTest::StartTests(const nsTArray<nsCString>& aProcessesList) {
     93  MOZ_ASSERT(NS_IsMainThread());
     94 
     95 #if defined(XP_WIN)
     96  nsCOMPtr<nsIFile> testFile;
     97  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(testFile));
     98  MOZ_ASSERT(testFile);
     99  nsCOMPtr<nsIFile> testChromeFile;
    100  testFile->Clone(getter_AddRefs(testChromeFile));
    101  testChromeFile->Append(u"chrome"_ns);
    102  testChromeFile->Exists(&mChromeDirExisted);
    103  testFile->Append(u"sandboxTest.txt"_ns);
    104  testChromeFile->Append(u"sandboxTest.txt"_ns);
    105  MOZ_ALWAYS_SUCCEEDS(testFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666));
    106  MOZ_ALWAYS_SUCCEEDS(testChromeFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666));
    107 #endif
    108 
    109  for (const auto& processTypeName : aProcessesList) {
    110    SandboxingKind sandboxingKind = SandboxingKind::COUNT;
    111    GeckoProcessType type = GeckoProcessType::GeckoProcessType_Invalid;
    112    if (processTypeName.Find(":") != kNotFound) {
    113      int32_t pos = processTypeName.Find(":");
    114      nsCString processType = nsCString(Substring(processTypeName, 0, pos));
    115      nsCString sandboxKindStr = nsCString(
    116          Substring(processTypeName, pos + 1, processTypeName.Length()));
    117 
    118      nsresult err;
    119      uint64_t sbVal = (uint64_t)(sandboxKindStr.ToDouble(&err));
    120      if (NS_FAILED(err)) {
    121        NS_WARNING("Unable to get SandboxingKind");
    122        return NS_ERROR_ILLEGAL_VALUE;
    123      }
    124 
    125      if (sbVal >= SandboxingKind::COUNT) {
    126        NS_WARNING("Invalid sandboxing kind");
    127        return NS_ERROR_ILLEGAL_VALUE;
    128      }
    129 
    130      if (!processType.Equals(
    131              XRE_GeckoProcessTypeToString(GeckoProcessType_Utility))) {
    132        NS_WARNING("Expected utility process type");
    133        return NS_ERROR_ILLEGAL_VALUE;
    134      }
    135 
    136      sandboxingKind = (SandboxingKind)sbVal;
    137      type = GeckoProcessType_Utility;
    138    } else {
    139      type = GeckoProcessStringToType(processTypeName);
    140 
    141      if (type == GeckoProcessType::GeckoProcessType_Invalid) {
    142        NS_WARNING("Invalid process type");
    143        return NS_ERROR_ILLEGAL_VALUE;
    144      }
    145    }
    146 
    147    RefPtr<ProcessPromise::Private> processPromise =
    148        MakeRefPtr<ProcessPromise::Private>(__func__);
    149 
    150    switch (type) {
    151      case GeckoProcessType_Content: {
    152        nsTArray<ContentParent*> parents;
    153        ContentParent::GetAll(parents);
    154        if (parents[0]) {
    155          InitializeSandboxTestingActors(parents[0], processPromise);
    156        } else {
    157          processPromise->Reject(NS_ERROR_FAILURE, __func__);
    158          MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get Content process");
    159        }
    160        break;
    161      }
    162 
    163      case GeckoProcessType_GPU: {
    164        gfx::GPUProcessManager* gpuProc = gfx::GPUProcessManager::Get();
    165        gfx::GPUChild* gpuChild = gpuProc ? gpuProc->GetGPUChild() : nullptr;
    166        if (gpuChild) {
    167          InitializeSandboxTestingActors(gpuChild, processPromise);
    168        } else {
    169          processPromise->Reject(NS_OK, __func__);
    170        }
    171        break;
    172      }
    173 
    174      case GeckoProcessType_RDD: {
    175        RDDProcessManager* rddProc = RDDProcessManager::Get();
    176        rddProc->LaunchRDDProcess()->Then(
    177            GetMainThreadSerialEventTarget(), __func__,
    178            [processPromise, rddProc]() {
    179              RDDChild* rddChild = rddProc ? rddProc->GetRDDChild() : nullptr;
    180              if (rddChild) {
    181                return InitializeSandboxTestingActors(rddChild, processPromise);
    182              }
    183              return processPromise->Reject(NS_ERROR_FAILURE, __func__);
    184            },
    185            [processPromise](nsresult aError) {
    186              MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get RDD process");
    187              return processPromise->Reject(aError, __func__);
    188            });
    189        break;
    190      }
    191 
    192      case GeckoProcessType_GMPlugin: {
    193        UnsetEnvVariable("MOZ_DISABLE_GMP_SANDBOX"_ns);
    194        RefPtr<gmp::GeckoMediaPluginService> service =
    195            gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
    196        MOZ_ASSERT(service, "We have a GeckoMediaPluginService");
    197 
    198        RefPtr<SandboxTest> self = this;
    199        nsCOMPtr<nsISerialEventTarget> thread = service->GetGMPThread();
    200        nsresult rv = thread->Dispatch(NS_NewRunnableFunction(
    201            "SandboxTest::GMPlugin", [self, processPromise, service, thread]() {
    202              service->GetContentParentForTest()->Then(
    203                  thread, __func__,
    204                  [self, processPromise](
    205                      const RefPtr<gmp::GMPContentParentCloseBlocker>&
    206                          wrapper) {
    207                    RefPtr<gmp::GMPContentParent> parent = wrapper->mParent;
    208                    MOZ_ASSERT(parent,
    209                               "Wrapper should wrap a valid parent if we're in "
    210                               "this path.");
    211                    if (!parent) {
    212                      return processPromise->Reject(NS_ERROR_ILLEGAL_VALUE,
    213                                                    __func__);
    214                    }
    215                    NS_DispatchToMainThread(NS_NewRunnableFunction(
    216                        "SandboxTesting::Wrapper", [self, wrapper]() {
    217                          self->mGMPContentParentWrapper = wrapper;
    218                        }));
    219                    return InitializeSandboxTestingActors(parent.get(),
    220                                                          processPromise);
    221                  },
    222                  [processPromise](const MediaResult& rv) {
    223                    return processPromise->Reject(NS_ERROR_FAILURE, __func__);
    224                  });
    225            }));
    226        NS_ENSURE_SUCCESS(rv, rv);
    227        break;
    228      }
    229 
    230      case GeckoProcessType_Socket: {
    231        // mochitest harness force this variable, but we actually do not want
    232        // that
    233        UnsetEnvVariable("MOZ_DISABLE_SOCKET_PROCESS"_ns);
    234 
    235        nsresult rv_pref =
    236            Preferences::SetBool("network.process.enabled", true);
    237        MOZ_ASSERT(rv_pref == NS_OK, "Error enforcing pref");
    238 
    239        MOZ_ASSERT(net::gIOService, "No gIOService?");
    240 
    241        net::gIOService->CallOrWaitForSocketProcess([processPromise]() {
    242          // If socket process was previously disabled by env,
    243          // nsIOService code will take some time before it creates the new
    244          // process and it triggers this callback
    245          RefPtr<net::SocketProcessParent> parent =
    246              net::SocketProcessParent::GetSingleton();
    247          if (parent) {
    248            return InitializeSandboxTestingActors(parent.get(), processPromise);
    249          }
    250          return processPromise->Reject(NS_ERROR_FAILURE, __func__);
    251        });
    252        break;
    253      }
    254 
    255      case GeckoProcessType_Utility: {
    256        RefPtr<UtilityProcessManager> utilityProc =
    257            UtilityProcessManager::GetSingleton();
    258        utilityProc->LaunchProcess(sandboxingKind)
    259            ->Then(
    260                GetMainThreadSerialEventTarget(), __func__,
    261                [processPromise, utilityProc, sandboxingKind]() {
    262                  RefPtr<UtilityProcessParent> utilityParent =
    263                      utilityProc
    264                          ? utilityProc->GetProcessParent(sandboxingKind)
    265                          : nullptr;
    266                  if (utilityParent) {
    267                    return InitializeSandboxTestingActors(utilityParent.get(),
    268                                                          processPromise);
    269                  }
    270                  return processPromise->Reject(NS_ERROR_FAILURE, __func__);
    271                },
    272                [processPromise](LaunchError const&) {
    273                  MOZ_ASSERT_UNREACHABLE(
    274                      "SandboxTest; failure to get Utility process");
    275                  return processPromise->Reject(NS_ERROR_FAILURE, __func__);
    276                });
    277        break;
    278      }
    279 
    280      default:
    281        MOZ_ASSERT_UNREACHABLE(
    282            "SandboxTest does not yet support this process type");
    283        return NS_ERROR_NOT_IMPLEMENTED;
    284    }
    285 
    286    RefPtr<SandboxTest> self = this;
    287    RefPtr<ProcessPromise> aPromise(processPromise);
    288    aPromise->Then(
    289        GetMainThreadSerialEventTarget(), __func__,
    290        [self](RefPtr<SandboxTestingParent> aValue) {
    291          self->mSandboxTestingParents.AppendElement(std::move(aValue));
    292          return NS_OK;
    293        },
    294        [](nsresult aError) {
    295          if (aError == NS_OK) {
    296            // There is no such process for this OS.  Report test done.
    297            nsCOMPtr<nsIObserverService> observerService =
    298                mozilla::services::GetObserverService();
    299            MOZ_RELEASE_ASSERT(observerService);
    300            observerService->NotifyObservers(nullptr, "sandbox-test-done",
    301                                             nullptr);
    302            return NS_OK;
    303          }
    304          MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get a process");
    305          return NS_ERROR_FAILURE;
    306        });
    307  }
    308  return NS_OK;
    309 }
    310 
    311 NS_IMETHODIMP
    312 SandboxTest::FinishTests() {
    313  if (mGMPContentParentWrapper) {
    314    RefPtr<gmp::GeckoMediaPluginService> service =
    315        gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
    316    MOZ_ASSERT(service, "We have a GeckoMediaPluginService");
    317 
    318    nsCOMPtr<nsISerialEventTarget> thread = service->GetGMPThread();
    319    nsresult rv = thread->Dispatch(NS_NewRunnableFunction(
    320        "SandboxTest::FinishTests",
    321        [wrapper = std::move(mGMPContentParentWrapper)]() {
    322          // Release mGMPContentWrapper's reference. We hold this to keep an
    323          // active reference on the CloseBlocker produced by GMPService,
    324          // otherwise it would automatically shutdown the GMPlugin thread we
    325          // started.
    326          // If somehow it does not work as expected, then tests will fail
    327          // because of leaks happening on GMPService and others.
    328        }));
    329    NS_ENSURE_SUCCESS(rv, rv);
    330  }
    331 
    332  for (RefPtr<SandboxTestingParent>& stp : mSandboxTestingParents) {
    333    SandboxTestingParent::Destroy(stp.forget());
    334  }
    335 
    336  // Make sure there is no leftover for test --verify to run without failure
    337  mSandboxTestingParents.Clear();
    338 
    339 #if defined(XP_WIN)
    340  nsCOMPtr<nsIFile> testFile;
    341  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(testFile));
    342  MOZ_ASSERT(testFile);
    343  nsCOMPtr<nsIFile> testChromeFile;
    344  testFile->Clone(getter_AddRefs(testChromeFile));
    345  testChromeFile->Append(u"chrome"_ns);
    346  testFile->Append(u"sandboxTest.txt"_ns);
    347  if (mChromeDirExisted) {
    348    // Chrome dir existed, just delete test file.
    349    testChromeFile->Append(u"sandboxTest.txt"_ns);
    350  }
    351  testFile->Remove(false);
    352  testChromeFile->Remove(true);
    353 #endif
    354 
    355  return NS_OK;
    356 }
    357 
    358 }  // namespace mozilla
    359 
    360 NS_IMPL_COMPONENT_FACTORY(mozISandboxTest) {
    361  return MakeAndAddRef<SandboxTest>().downcast<nsISupports>();
    362 }