summaryrefslogtreecommitdiffstats
path: root/security/sandbox/common/test/SandboxTest.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /security/sandbox/common/test/SandboxTest.cpp
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/sandbox/common/test/SandboxTest.cpp')
-rw-r--r--security/sandbox/common/test/SandboxTest.cpp362
1 files changed, 362 insertions, 0 deletions
diff --git a/security/sandbox/common/test/SandboxTest.cpp b/security/sandbox/common/test/SandboxTest.cpp
new file mode 100644
index 0000000000..883a6e2203
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTest.cpp
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "SandboxTest.h"
+
+#include "mozilla/Components.h"
+#include "mozilla/Preferences.h"
+#include "SandboxTestingParent.h"
+#include "SandboxTestingChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/GPUChild.h"
+#include "mozilla/net/SocketProcessParent.h"
+#include "mozilla/RDDProcessManager.h"
+#include "mozilla/RDDChild.h"
+#include "mozilla/ipc/UtilityProcessManager.h"
+#include "mozilla/ipc/UtilityProcessParent.h"
+#include "mozilla/ipc/UtilityProcessSandboxing.h"
+#include "GMPService.h"
+#include "mozilla/gmp/GMPTypes.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "nsIOService.h"
+
+#ifdef XP_WIN
+# include "nsAppDirectoryServiceDefs.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+using namespace mozilla::dom;
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(SandboxTest, mozISandboxTest)
+
+inline void UnsetEnvVariable(const nsCString& aEnvVarName) {
+ nsCString aEnvVarNameFull = aEnvVarName + "="_ns;
+ int rv_unset =
+#ifdef XP_UNIX
+ unsetenv(aEnvVarName.get());
+#endif // XP_UNIX
+#ifdef XP_WIN
+ _putenv(aEnvVarNameFull.get());
+#endif // XP_WIN
+ MOZ_ASSERT(rv_unset == 0, "Error unsetting env var");
+}
+
+GeckoProcessType GeckoProcessStringToType(const nsCString& aString) {
+ for (GeckoProcessType type = GeckoProcessType(0);
+ type < GeckoProcessType::GeckoProcessType_End;
+ type = GeckoProcessType(type + 1)) {
+ if (aString == XRE_GeckoProcessTypeToString(type)) {
+ return type;
+ }
+ }
+ return GeckoProcessType::GeckoProcessType_Invalid;
+}
+
+// Set up tests on remote process connected to the given actor.
+// The actor must handle the InitSandboxTesting message.
+template <typename Actor>
+void InitializeSandboxTestingActors(
+ Actor* aActor,
+ const RefPtr<SandboxTest::ProcessPromise::Private>& aProcessPromise) {
+ MOZ_ASSERT(aActor, "Should have provided an IPC actor");
+ Endpoint<PSandboxTestingParent> sandboxTestingParentEnd;
+ Endpoint<PSandboxTestingChild> sandboxTestingChildEnd;
+ nsresult rv = PSandboxTesting::CreateEndpoints(&sandboxTestingParentEnd,
+ &sandboxTestingChildEnd);
+ if (NS_FAILED(rv)) {
+ aProcessPromise->Reject(NS_ERROR_FAILURE, __func__);
+ return;
+ }
+
+ // GMPlugin binds us to the GMP Thread, so we need IPC's Send to be done on
+ // the same thread
+ Unused << aActor->SendInitSandboxTesting(std::move(sandboxTestingChildEnd));
+ // But then the SandboxTestingParent::Create() call needs to be on the main
+ // thread
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "SandboxTestingParent::Create",
+ [stpE = std::move(sandboxTestingParentEnd), aProcessPromise]() mutable {
+ return aProcessPromise->Resolve(
+ SandboxTestingParent::Create(std::move(stpE)), __func__);
+ }));
+}
+
+NS_IMETHODIMP
+SandboxTest::StartTests(const nsTArray<nsCString>& aProcessesList) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+#if defined(XP_WIN)
+ nsCOMPtr<nsIFile> testFile;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(testFile));
+ MOZ_ASSERT(testFile);
+ nsCOMPtr<nsIFile> testChromeFile;
+ testFile->Clone(getter_AddRefs(testChromeFile));
+ testChromeFile->Append(u"chrome"_ns);
+ testChromeFile->Exists(&mChromeDirExisted);
+ testFile->Append(u"sandboxTest.txt"_ns);
+ testChromeFile->Append(u"sandboxTest.txt"_ns);
+ MOZ_ALWAYS_SUCCEEDS(testFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666));
+ MOZ_ALWAYS_SUCCEEDS(testChromeFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666));
+#endif
+
+ for (const auto& processTypeName : aProcessesList) {
+ SandboxingKind sandboxingKind = SandboxingKind::COUNT;
+ GeckoProcessType type = GeckoProcessType::GeckoProcessType_Invalid;
+ if (processTypeName.Find(":") != kNotFound) {
+ int32_t pos = processTypeName.Find(":");
+ nsCString processType = nsCString(Substring(processTypeName, 0, pos));
+ nsCString sandboxKindStr = nsCString(
+ Substring(processTypeName, pos + 1, processTypeName.Length()));
+
+ nsresult err;
+ uint64_t sbVal = (uint64_t)(sandboxKindStr.ToDouble(&err));
+ if (NS_FAILED(err)) {
+ NS_WARNING("Unable to get SandboxingKind");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if (sbVal >= SandboxingKind::COUNT) {
+ NS_WARNING("Invalid sandboxing kind");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if (!processType.Equals(
+ XRE_GeckoProcessTypeToString(GeckoProcessType_Utility))) {
+ NS_WARNING("Expected utility process type");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ sandboxingKind = (SandboxingKind)sbVal;
+ type = GeckoProcessType_Utility;
+ } else {
+ type = GeckoProcessStringToType(processTypeName);
+
+ if (type == GeckoProcessType::GeckoProcessType_Invalid) {
+ NS_WARNING("Invalid process type");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ }
+
+ RefPtr<ProcessPromise::Private> processPromise =
+ MakeRefPtr<ProcessPromise::Private>(__func__);
+
+ switch (type) {
+ case GeckoProcessType_Content: {
+ nsTArray<ContentParent*> parents;
+ ContentParent::GetAll(parents);
+ if (parents[0]) {
+ InitializeSandboxTestingActors(parents[0], processPromise);
+ } else {
+ processPromise->Reject(NS_ERROR_FAILURE, __func__);
+ MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get Content process");
+ }
+ break;
+ }
+
+ case GeckoProcessType_GPU: {
+ gfx::GPUProcessManager* gpuProc = gfx::GPUProcessManager::Get();
+ gfx::GPUChild* gpuChild = gpuProc ? gpuProc->GetGPUChild() : nullptr;
+ if (gpuChild) {
+ InitializeSandboxTestingActors(gpuChild, processPromise);
+ } else {
+ processPromise->Reject(NS_OK, __func__);
+ }
+ break;
+ }
+
+ case GeckoProcessType_RDD: {
+ RDDProcessManager* rddProc = RDDProcessManager::Get();
+ rddProc->LaunchRDDProcess()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [processPromise, rddProc]() {
+ RDDChild* rddChild = rddProc ? rddProc->GetRDDChild() : nullptr;
+ if (rddChild) {
+ return InitializeSandboxTestingActors(rddChild, processPromise);
+ }
+ return processPromise->Reject(NS_ERROR_FAILURE, __func__);
+ },
+ [processPromise](nsresult aError) {
+ MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get RDD process");
+ return processPromise->Reject(aError, __func__);
+ });
+ break;
+ }
+
+ case GeckoProcessType_GMPlugin: {
+ UnsetEnvVariable("MOZ_DISABLE_GMP_SANDBOX"_ns);
+ RefPtr<gmp::GeckoMediaPluginService> service =
+ gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
+ MOZ_ASSERT(service, "We have a GeckoMediaPluginService");
+
+ RefPtr<SandboxTest> self = this;
+ nsCOMPtr<nsISerialEventTarget> thread = service->GetGMPThread();
+ nsresult rv = thread->Dispatch(NS_NewRunnableFunction(
+ "SandboxTest::GMPlugin", [self, processPromise, service, thread]() {
+ service->GetContentParentForTest()->Then(
+ thread, __func__,
+ [self, processPromise](
+ const RefPtr<gmp::GMPContentParentCloseBlocker>&
+ wrapper) {
+ RefPtr<gmp::GMPContentParent> parent = wrapper->mParent;
+ MOZ_ASSERT(parent,
+ "Wrapper should wrap a valid parent if we're in "
+ "this path.");
+ if (!parent) {
+ return processPromise->Reject(NS_ERROR_ILLEGAL_VALUE,
+ __func__);
+ }
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "SandboxTesting::Wrapper", [self, wrapper]() {
+ self->mGMPContentParentWrapper = wrapper;
+ }));
+ return InitializeSandboxTestingActors(parent.get(),
+ processPromise);
+ },
+ [processPromise](const MediaResult& rv) {
+ return processPromise->Reject(NS_ERROR_FAILURE, __func__);
+ });
+ }));
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ }
+
+ case GeckoProcessType_Socket: {
+ // mochitest harness force this variable, but we actually do not want
+ // that
+ UnsetEnvVariable("MOZ_DISABLE_SOCKET_PROCESS"_ns);
+
+ nsresult rv_pref =
+ Preferences::SetBool("network.process.enabled", true);
+ MOZ_ASSERT(rv_pref == NS_OK, "Error enforcing pref");
+
+ MOZ_ASSERT(net::gIOService, "No gIOService?");
+
+ net::gIOService->CallOrWaitForSocketProcess([processPromise]() {
+ // If socket process was previously disabled by env,
+ // nsIOService code will take some time before it creates the new
+ // process and it triggers this callback
+ net::SocketProcessParent* parent =
+ net::SocketProcessParent::GetSingleton();
+ if (parent) {
+ return InitializeSandboxTestingActors(parent, processPromise);
+ }
+ return processPromise->Reject(NS_ERROR_FAILURE, __func__);
+ });
+ break;
+ }
+
+ case GeckoProcessType_Utility: {
+ RefPtr<UtilityProcessManager> utilityProc =
+ UtilityProcessManager::GetSingleton();
+ utilityProc->LaunchProcess(sandboxingKind)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [processPromise, utilityProc, sandboxingKind]() {
+ RefPtr<UtilityProcessParent> utilityParent =
+ utilityProc
+ ? utilityProc->GetProcessParent(sandboxingKind)
+ : nullptr;
+ if (utilityParent) {
+ return InitializeSandboxTestingActors(utilityParent.get(),
+ processPromise);
+ }
+ return processPromise->Reject(NS_ERROR_FAILURE, __func__);
+ },
+ [processPromise](nsresult aError) {
+ MOZ_ASSERT_UNREACHABLE(
+ "SandboxTest; failure to get Utility process");
+ return processPromise->Reject(aError, __func__);
+ });
+ break;
+ }
+
+ default:
+ MOZ_ASSERT_UNREACHABLE(
+ "SandboxTest does not yet support this process type");
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ RefPtr<SandboxTest> self = this;
+ RefPtr<ProcessPromise> aPromise(processPromise);
+ aPromise->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [self](RefPtr<SandboxTestingParent> aValue) {
+ self->mSandboxTestingParents.AppendElement(std::move(aValue));
+ return NS_OK;
+ },
+ [](nsresult aError) {
+ if (aError == NS_OK) {
+ // There is no such process for this OS. Report test done.
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ MOZ_RELEASE_ASSERT(observerService);
+ observerService->NotifyObservers(nullptr, "sandbox-test-done",
+ nullptr);
+ return NS_OK;
+ }
+ MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get a process");
+ return NS_ERROR_FAILURE;
+ });
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SandboxTest::FinishTests() {
+ if (mGMPContentParentWrapper) {
+ RefPtr<gmp::GeckoMediaPluginService> service =
+ gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
+ MOZ_ASSERT(service, "We have a GeckoMediaPluginService");
+
+ nsCOMPtr<nsISerialEventTarget> thread = service->GetGMPThread();
+ nsresult rv = thread->Dispatch(NS_NewRunnableFunction(
+ "SandboxTest::FinishTests",
+ [wrapper = std::move(mGMPContentParentWrapper)]() {
+ // Release mGMPContentWrapper's reference. We hold this to keep an
+ // active reference on the CloseBlocker produced by GMPService,
+ // otherwise it would automatically shutdown the GMPlugin thread we
+ // started.
+ // If somehow it does not work as expected, then tests will fail
+ // because of leaks happening on GMPService and others.
+ }));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ for (RefPtr<SandboxTestingParent>& stp : mSandboxTestingParents) {
+ SandboxTestingParent::Destroy(stp.forget());
+ }
+
+ // Make sure there is no leftover for test --verify to run without failure
+ mSandboxTestingParents.Clear();
+
+#if defined(XP_WIN)
+ nsCOMPtr<nsIFile> testFile;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(testFile));
+ MOZ_ASSERT(testFile);
+ nsCOMPtr<nsIFile> testChromeFile;
+ testFile->Clone(getter_AddRefs(testChromeFile));
+ testChromeFile->Append(u"chrome"_ns);
+ testFile->Append(u"sandboxTest.txt"_ns);
+ if (mChromeDirExisted) {
+ // Chrome dir existed, just delete test file.
+ testChromeFile->Append(u"sandboxTest.txt"_ns);
+ }
+ testFile->Remove(false);
+ testChromeFile->Remove(true);
+#endif
+
+ return NS_OK;
+}
+
+} // namespace mozilla
+
+NS_IMPL_COMPONENT_FACTORY(mozISandboxTest) {
+ return MakeAndAddRef<SandboxTest>().downcast<nsISupports>();
+}