summaryrefslogtreecommitdiffstats
path: root/security/sandbox/common
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /security/sandbox/common
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/sandbox/common')
-rw-r--r--security/sandbox/common/SandboxSettings.cpp94
-rw-r--r--security/sandbox/common/SandboxSettings.h30
-rw-r--r--security/sandbox/common/components.conf23
-rw-r--r--security/sandbox/common/moz.build50
-rw-r--r--security/sandbox/common/mozISandboxSettings.idl25
-rw-r--r--security/sandbox/common/test/PSandboxTesting.ipdl18
-rw-r--r--security/sandbox/common/test/SandboxTest.cpp112
-rw-r--r--security/sandbox/common/test/SandboxTest.h33
-rw-r--r--security/sandbox/common/test/SandboxTestingChild.cpp139
-rw-r--r--security/sandbox/common/test/SandboxTestingChild.h66
-rw-r--r--security/sandbox/common/test/SandboxTestingParent.cpp108
-rw-r--r--security/sandbox/common/test/SandboxTestingParent.h50
-rw-r--r--security/sandbox/common/test/SandboxTestingThread.h53
-rw-r--r--security/sandbox/common/test/mozISandboxTest.idl28
14 files changed, 829 insertions, 0 deletions
diff --git a/security/sandbox/common/SandboxSettings.cpp b/security/sandbox/common/SandboxSettings.cpp
new file mode 100644
index 0000000000..c3f716e484
--- /dev/null
+++ b/security/sandbox/common/SandboxSettings.cpp
@@ -0,0 +1,94 @@
+/* -*- 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 http://mozilla.org/MPL/2.0/. */
+
+#include "mozISandboxSettings.h"
+
+#include "mozilla/Components.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/StaticPrefs_security.h"
+
+#include "prenv.h"
+
+using namespace mozilla;
+
+namespace mozilla {
+
+int GetEffectiveContentSandboxLevel() {
+ if (PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
+ return 0;
+ }
+ int level = StaticPrefs::security_sandbox_content_level_DoNotUseDirectly();
+// On Windows and macOS, enforce a minimum content sandbox level of 1 (except on
+// Nightly, where it can be set to 0).
+#if !defined(NIGHTLY_BUILD) && (defined(XP_WIN) || defined(XP_MACOSX))
+ if (level < 1) {
+ level = 1;
+ }
+#endif
+#ifdef XP_LINUX
+ // Level 4 and up will break direct access to audio.
+ if (level > 3 && !StaticPrefs::media_cubeb_sandbox()) {
+ level = 3;
+ }
+#endif
+
+ return level;
+}
+
+bool IsContentSandboxEnabled() { return GetEffectiveContentSandboxLevel() > 0; }
+
+int GetEffectiveSocketProcessSandboxLevel() {
+ if (PR_GetEnv("MOZ_DISABLE_SOCKET_PROCESS_SANDBOX")) {
+ return 0;
+ }
+
+ int level =
+ StaticPrefs::security_sandbox_socket_process_level_DoNotUseDirectly();
+
+ return level;
+}
+
+#if defined(XP_MACOSX)
+int ClampFlashSandboxLevel(const int aLevel) {
+ const int minLevel = 0;
+ const int maxLevel = 3;
+
+ if (aLevel < minLevel) {
+ return minLevel;
+ }
+
+ if (aLevel > maxLevel) {
+ return maxLevel;
+ }
+ return aLevel;
+}
+#endif
+
+class SandboxSettings final : public mozISandboxSettings {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_MOZISANDBOXSETTINGS
+
+ SandboxSettings() = default;
+
+ private:
+ ~SandboxSettings() = default;
+};
+
+NS_IMPL_ISUPPORTS(SandboxSettings, mozISandboxSettings)
+
+NS_IMETHODIMP SandboxSettings::GetEffectiveContentSandboxLevel(
+ int32_t* aRetVal) {
+ *aRetVal = mozilla::GetEffectiveContentSandboxLevel();
+ return NS_OK;
+}
+
+} // namespace mozilla
+
+NS_IMPL_COMPONENT_FACTORY(mozISandboxSettings) {
+ return MakeAndAddRef<SandboxSettings>().downcast<nsISupports>();
+}
diff --git a/security/sandbox/common/SandboxSettings.h b/security/sandbox/common/SandboxSettings.h
new file mode 100644
index 0000000000..9161995c43
--- /dev/null
+++ b/security/sandbox/common/SandboxSettings.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SandboxSettings_h
+#define mozilla_SandboxSettings_h
+
+namespace mozilla {
+
+// Return the current sandbox level. This is the
+// "security.sandbox.content.level" preference, but rounded up to the current
+// minimum allowed level. Returns 0 (disabled) if the env var
+// MOZ_DISABLE_CONTENT_SANDBOX is set.
+int GetEffectiveContentSandboxLevel();
+int GetEffectiveSocketProcessSandboxLevel();
+
+// Checks whether the effective content sandbox level is > 0.
+bool IsContentSandboxEnabled();
+
+#if defined(XP_MACOSX)
+int ClampFlashSandboxLevel(const int aLevel);
+#endif
+
+#if defined(__OpenBSD__)
+bool StartOpenBSDSandbox(GeckoProcessType type);
+#endif
+
+} // namespace mozilla
+#endif // mozilla_SandboxPolicies_h
diff --git a/security/sandbox/common/components.conf b/security/sandbox/common/components.conf
new file mode 100644
index 0000000000..d538b7e832
--- /dev/null
+++ b/security/sandbox/common/components.conf
@@ -0,0 +1,23 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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 http://mozilla.org/MPL/2.0/.
+
+Classes = [
+ {
+ 'cid': '{5516303d-9007-45a0-94b9-940ef134a6e2}',
+ 'contract_ids': ['@mozilla.org/sandbox/sandbox-settings;1'],
+ 'type': 'mozISandboxSettings',
+ },
+]
+
+if defined('MOZ_SANDBOX') and defined('MOZ_DEBUG') and defined('ENABLE_TESTS'):
+ Classes += [
+ {
+ 'cid':
+ '{2306c118-3544-4674-9222-670b88dc07a9}',
+ 'contract_ids': ['@mozilla.org/sandbox/sandbox-test;1'],
+ 'type': 'mozISandboxTest',
+ },
+]
diff --git a/security/sandbox/common/moz.build b/security/sandbox/common/moz.build
new file mode 100644
index 0000000000..384fe245f7
--- /dev/null
+++ b/security/sandbox/common/moz.build
@@ -0,0 +1,50 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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 http://mozilla.org/MPL/2.0/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Security: Process Sandboxing")
+
+UNIFIED_SOURCES += [
+ "SandboxSettings.cpp",
+]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+XPIDL_SOURCES += [
+ "mozISandboxSettings.idl",
+]
+
+XPIDL_MODULE = "sandbox"
+
+if CONFIG["MOZ_SANDBOX"] and CONFIG["MOZ_DEBUG"] and CONFIG["ENABLE_TESTS"]:
+ UNIFIED_SOURCES += [
+ "test/SandboxTest.cpp",
+ "test/SandboxTestingChild.cpp",
+ "test/SandboxTestingParent.cpp",
+ ]
+
+ EXPORTS.mozilla += [
+ "test/SandboxTestingChild.h",
+ "test/SandboxTestingParent.h",
+ ]
+
+ IPDL_SOURCES += [
+ "test/PSandboxTesting.ipdl",
+ ]
+
+ XPIDL_SOURCES += [
+ "test/mozISandboxTest.idl",
+ ]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+EXPORTS.mozilla += [
+ "SandboxSettings.h",
+]
diff --git a/security/sandbox/common/mozISandboxSettings.idl b/security/sandbox/common/mozISandboxSettings.idl
new file mode 100644
index 0000000000..0e2a879c2b
--- /dev/null
+++ b/security/sandbox/common/mozISandboxSettings.idl
@@ -0,0 +1,25 @@
+/* -*- Mode: IDL; 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 http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/* Used to expose information about the configuration of the sanbox.
+ */
+[scriptable, builtinclass, uuid(5516303d-9007-45a0-94b9-940ef134a6e2)]
+interface mozISandboxSettings : nsISupports
+{
+ readonly attribute long effectiveContentSandboxLevel;
+};
+
+%{ C++
+
+#define MOZ_SANDBOX_SETTINGS_CID \
+{0x5516303d, 0x9007, 0x45a0, { 0x94, 0xb9, 0x94, 0x0e, 0xf1, 0x34, 0xa6, 0xe2}}
+
+#define MOZ_SANDBOX_SETTINGS_CONTRACTID \
+ "@mozilla.org/sandbox/sandbox-settings;1"
+
+%}
diff --git a/security/sandbox/common/test/PSandboxTesting.ipdl b/security/sandbox/common/test/PSandboxTesting.ipdl
new file mode 100644
index 0000000000..0acf86ecdc
--- /dev/null
+++ b/security/sandbox/common/test/PSandboxTesting.ipdl
@@ -0,0 +1,18 @@
+/* -*- 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/. */
+
+namespace mozilla {
+
+async protocol PSandboxTesting {
+parent:
+ async ReportTestResults(nsCString testName, bool shouldSucceed, bool didSucceed, nsCString resultMessage);
+ async TestCompleted();
+
+child:
+ async ShutDown();
+};
+
+} //namespace mozilla
diff --git a/security/sandbox/common/test/SandboxTest.cpp b/security/sandbox/common/test/SandboxTest.cpp
new file mode 100644
index 0000000000..5996dabb13
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTest.cpp
@@ -0,0 +1,112 @@
+/* -*- 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 "SandboxTestingParent.h"
+#include "SandboxTestingChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/GPUChild.h"
+#include "mozilla/ipc/Endpoint.h"
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+using namespace mozilla::dom;
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(SandboxTest, mozISandboxTest)
+
+GeckoProcessType GeckoProcessStringToType(const nsCString& aString) {
+ for (GeckoProcessType type = GeckoProcessType(0);
+ type < GeckoProcessType::GeckoProcessType_End;
+ type = GeckoProcessType(type + 1)) {
+ if (aString == kGeckoProcessTypeString[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>
+SandboxTestingParent* InitializeSandboxTestingActors(Actor* aActor) {
+ Endpoint<PSandboxTestingParent> sandboxTestingParentEnd;
+ Endpoint<PSandboxTestingChild> sandboxTestingChildEnd;
+ nsresult rv = PSandboxTesting::CreateEndpoints(
+ base::GetCurrentProcId(), aActor->OtherPid(), &sandboxTestingParentEnd,
+ &sandboxTestingChildEnd);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ Unused << aActor->SendInitSandboxTesting(std::move(sandboxTestingChildEnd));
+ return SandboxTestingParent::Create(std::move(sandboxTestingParentEnd));
+}
+
+NS_IMETHODIMP
+SandboxTest::StartTests(const nsTArray<nsCString>& aProcessesList) {
+ for (auto processTypeName : aProcessesList) {
+ GeckoProcessType type = GeckoProcessStringToType(processTypeName);
+ if (type == GeckoProcessType::GeckoProcessType_Invalid) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ switch (type) {
+ case GeckoProcessType_Content: {
+ nsTArray<ContentParent*> parents;
+ ContentParent::GetAll(parents);
+ MOZ_ASSERT(parents.Length() > 0);
+ mSandboxTestingParents[type] =
+ InitializeSandboxTestingActors(parents[0]);
+ break;
+ }
+
+ case GeckoProcessType_GPU: {
+ gfx::GPUProcessManager* gpuProc = gfx::GPUProcessManager::Get();
+ gfx::GPUChild* gpuChild = gpuProc ? gpuProc->GetGPUChild() : nullptr;
+ if (!gpuChild) {
+ // There is no GPU process for this OS. Report test done.
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ MOZ_RELEASE_ASSERT(observerService);
+ observerService->NotifyObservers(nullptr, "sandbox-test-done", 0);
+ return NS_OK;
+ }
+
+ mSandboxTestingParents[type] = InitializeSandboxTestingActors(gpuChild);
+ break;
+ }
+
+ default:
+ MOZ_ASSERT_UNREACHABLE(
+ "SandboxTest does not yet support this process type");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if (!mSandboxTestingParents[type]) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SandboxTest::FinishTests() {
+ for (SandboxTestingParent* stp : mSandboxTestingParents) {
+ SandboxTestingParent::Destroy(stp);
+ }
+ return NS_OK;
+}
+
+} // namespace mozilla
+
+NS_IMPL_COMPONENT_FACTORY(mozISandboxTest) {
+ return MakeAndAddRef<SandboxTest>().downcast<nsISupports>();
+}
diff --git a/security/sandbox/common/test/SandboxTest.h b/security/sandbox/common/test/SandboxTest.h
new file mode 100644
index 0000000000..2fecf9e3fe
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTest.h
@@ -0,0 +1,33 @@
+/* -*- 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/. */
+#ifndef mozilla_SandboxTest_h
+#define mozilla_SandboxTest_h
+
+#include "SandboxTestingParent.h"
+#include "mozISandboxTest.h"
+#include "mozilla/GfxMessageUtils.h"
+
+#if !defined(MOZ_DEBUG) || !defined(ENABLE_TESTS)
+# error "This file should not be used outside of debug with tests"
+#endif
+
+namespace mozilla {
+class SandboxTest : public mozISandboxTest {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_MOZISANDBOXTEST
+
+ SandboxTest() : mSandboxTestingParents{nullptr} {};
+
+ private:
+ virtual ~SandboxTest() = default;
+ static constexpr size_t NumProcessTypes =
+ static_cast<size_t>(GeckoProcessType_End);
+ SandboxTestingParent* mSandboxTestingParents[NumProcessTypes];
+};
+
+} // namespace mozilla
+#endif // mozilla_SandboxTest_h
diff --git a/security/sandbox/common/test/SandboxTestingChild.cpp b/security/sandbox/common/test/SandboxTestingChild.cpp
new file mode 100644
index 0000000000..be68b2ba46
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTestingChild.cpp
@@ -0,0 +1,139 @@
+/* -*- 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 "SandboxTestingChild.h"
+#include "SandboxTestingThread.h"
+
+#include "nsXULAppAPI.h"
+
+#ifdef XP_UNIX
+# include <fcntl.h>
+# include <sys/stat.h>
+# include <sys/types.h>
+# include <time.h>
+# include <unistd.h>
+#endif
+
+namespace mozilla {
+
+SandboxTestingChild* SandboxTestingChild::sInstance = nullptr;
+
+bool SandboxTestingChild::IsTestThread() { return mThread->IsOnThread(); }
+
+void SandboxTestingChild::PostToTestThread(
+ already_AddRefed<nsIRunnable>&& runnable) {
+ mThread->Dispatch(std::move(runnable));
+}
+
+/* static */
+bool SandboxTestingChild::Initialize(
+ Endpoint<PSandboxTestingChild>&& aSandboxTestingEndpoint) {
+ MOZ_ASSERT(!sInstance);
+ SandboxTestingThread* thread = SandboxTestingThread::Create();
+ if (!thread) {
+ return false;
+ }
+ sInstance =
+ new SandboxTestingChild(thread, std::move(aSandboxTestingEndpoint));
+ return true;
+}
+
+/* static */
+SandboxTestingChild* SandboxTestingChild::GetInstance() {
+ MOZ_ASSERT(sInstance, "Must initialize SandboxTestingChild before using it");
+ return sInstance;
+}
+
+SandboxTestingChild::SandboxTestingChild(
+ SandboxTestingThread* aThread, Endpoint<PSandboxTestingChild>&& aEndpoint)
+ : mThread(aThread) {
+ MOZ_ASSERT(aThread);
+ PostToTestThread(NewNonOwningRunnableMethod<Endpoint<PSandboxTestingChild>&&>(
+ "SandboxTestingChild::Bind", this, &SandboxTestingChild::Bind,
+ std::move(aEndpoint)));
+}
+
+void SandboxTestingChild::Bind(Endpoint<PSandboxTestingChild>&& aEndpoint) {
+ MOZ_RELEASE_ASSERT(mThread->IsOnThread());
+ DebugOnly<bool> ok = aEndpoint.Bind(this);
+ MOZ_ASSERT(ok);
+
+ if (XRE_IsContentProcess()) {
+#ifdef XP_UNIX
+ struct stat st;
+ static const char kAllowedPath[] = "/usr/lib";
+
+ ErrnoTest("fstatat_as_stat"_ns, true,
+ [&] { return fstatat(AT_FDCWD, kAllowedPath, &st, 0); });
+ ErrnoTest("fstatat_as_lstat"_ns, true, [&] {
+ return fstatat(AT_FDCWD, kAllowedPath, &st, AT_SYMLINK_NOFOLLOW);
+ });
+
+# ifdef XP_LINUX
+ ErrnoTest("fstatat_as_fstat"_ns, true,
+ [&] { return fstatat(0, "", &st, AT_EMPTY_PATH); });
+# endif // XP_LINUX
+
+ const struct timespec usec = {0, 1000};
+ ErrnoTest("nanosleep"_ns, true, [&] { return nanosleep(&usec, nullptr); });
+
+ struct timespec res = {0, 0};
+ ErrnoTest("clock_getres"_ns, true,
+ [&] { return clock_getres(CLOCK_REALTIME, &res); });
+
+#else // XP_UNIX
+ SendReportTestResults("dummy_test"_ns,
+ /* shouldSucceed */ true,
+ /* didSucceed */ true,
+ "The test framework fails if there are no cases."_ns);
+#endif // XP_UNIX
+ }
+
+ // Tell SandboxTest that this process is done with all tests.
+ SendTestCompleted();
+}
+
+void SandboxTestingChild::ActorDestroy(ActorDestroyReason aWhy) {
+ MOZ_ASSERT(mThread->IsOnThread());
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "SandboxChildDestroyer", []() { SandboxTestingChild::Destroy(); }));
+}
+
+void SandboxTestingChild::Destroy() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(sInstance);
+ delete sInstance;
+ sInstance = nullptr;
+}
+
+bool SandboxTestingChild::RecvShutDown() {
+ Close();
+ return true;
+}
+
+#ifdef XP_UNIX
+template <typename F>
+void SandboxTestingChild::ErrnoTest(const nsCString& aName, bool aExpectSuccess,
+ F&& aFunction) {
+ int status = aFunction() >= 0 ? 0 : errno;
+ PosixTest(aName, aExpectSuccess, status);
+}
+
+void SandboxTestingChild::PosixTest(const nsCString& aName, bool aExpectSuccess,
+ int aStatus) {
+ bool succeeded = aStatus == 0;
+ nsAutoCString message;
+ if (succeeded) {
+ message = "Succeeded"_ns;
+ } else {
+ message.AppendPrintf("Error: %s", strerror(aStatus));
+ }
+
+ SendReportTestResults(aName, aExpectSuccess, succeeded, message);
+}
+#endif // XP_UNIX
+
+} // namespace mozilla
diff --git a/security/sandbox/common/test/SandboxTestingChild.h b/security/sandbox/common/test/SandboxTestingChild.h
new file mode 100644
index 0000000000..4ac3f577f5
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTestingChild.h
@@ -0,0 +1,66 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxTestingChild_h
+#define mozilla_SandboxTestingChild_h
+
+#include "mozilla/PSandboxTestingChild.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/UniquePtr.h"
+
+#ifdef XP_UNIX
+# include "nsString.h"
+#endif
+
+#if !defined(MOZ_SANDBOX) || !defined(MOZ_DEBUG) || !defined(ENABLE_TESTS)
+# error "This file should not be used outside of debug with tests"
+#endif
+
+namespace mozilla {
+
+class SandboxTestingThread;
+
+/**
+ * Runs tests that check sandbox in child process, depending on process type.
+ */
+class SandboxTestingChild : public PSandboxTestingChild {
+ public:
+ static bool Initialize(
+ Endpoint<PSandboxTestingChild>&& aSandboxTestingEndpoint);
+ static SandboxTestingChild* GetInstance();
+ static void Destroy();
+
+ bool IsTestThread();
+ void PostToTestThread(already_AddRefed<nsIRunnable>&& runnable);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual bool RecvShutDown();
+
+ private:
+ explicit SandboxTestingChild(SandboxTestingThread* aThread,
+ Endpoint<PSandboxTestingChild>&& aEndpoint);
+ void Bind(Endpoint<PSandboxTestingChild>&& aEndpoint);
+
+#ifdef XP_UNIX
+ // For test cases that return an error number or 0, like newer POSIX APIs.
+ void PosixTest(const nsCString& aName, bool aExpectSuccess, int aStatus);
+
+ // For test cases that return a negative number and set `errno` to
+ // indicate error, like classical Unix APIs; takes a callable, which
+ // is used only in this function call (so `[&]` captures are safe).
+ template <typename F>
+ void ErrnoTest(const nsCString& aName, bool aExpectSuccess, F&& aFunction);
+#endif
+
+ UniquePtr<SandboxTestingThread> mThread;
+
+ static SandboxTestingChild* sInstance;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxTestingChild_h
diff --git a/security/sandbox/common/test/SandboxTestingParent.cpp b/security/sandbox/common/test/SandboxTestingParent.cpp
new file mode 100644
index 0000000000..a787088e7b
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTestingParent.cpp
@@ -0,0 +1,108 @@
+/* -*- 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 "SandboxTestingParent.h"
+#include "SandboxTestingThread.h"
+
+namespace mozilla {
+
+/* static */
+SandboxTestingParent* SandboxTestingParent::Create(
+ Endpoint<PSandboxTestingParent>&& aParentEnd) {
+ SandboxTestingThread* thread = SandboxTestingThread::Create();
+ if (!thread) {
+ return nullptr;
+ }
+ return new SandboxTestingParent(thread, std::move(aParentEnd));
+}
+
+SandboxTestingParent::SandboxTestingParent(
+ SandboxTestingThread* aThread, Endpoint<PSandboxTestingParent>&& aParentEnd)
+ : mThread(aThread),
+ mMonitor("SandboxTestingParent Lock"),
+ mShutdownDone(false) {
+ MOZ_ASSERT(mThread);
+ mThread->Dispatch(
+ NewNonOwningRunnableMethod<Endpoint<PSandboxTestingParent>&&>(
+ "SandboxTestingParent::Bind", this, &SandboxTestingParent::Bind,
+ std::move(aParentEnd)));
+}
+
+void SandboxTestingParent::Bind(Endpoint<PSandboxTestingParent>&& aEnd) {
+ MOZ_RELEASE_ASSERT(mThread->IsOnThread());
+ DebugOnly<bool> ok = aEnd.Bind(this);
+ MOZ_ASSERT(ok);
+}
+
+void SandboxTestingParent::ShutdownSandboxTestThread() {
+ MOZ_ASSERT(mThread->IsOnThread());
+ Close();
+ // Notify waiting thread that we are done.
+ MonitorAutoLock lock(mMonitor);
+ mShutdownDone = true;
+ mMonitor.Notify();
+}
+
+void SandboxTestingParent::Destroy(SandboxTestingParent* aInstance) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!aInstance) {
+ return;
+ }
+
+ {
+ // Hold the lock while we destroy the actor on the test thread.
+ MonitorAutoLock lock(aInstance->mMonitor);
+ aInstance->mThread->Dispatch(NewNonOwningRunnableMethod(
+ "SandboxTestingParent::ShutdownSandboxTestThread", aInstance,
+ &SandboxTestingParent::ShutdownSandboxTestThread));
+
+ // Wait for test thread to complete destruction.
+ while (!aInstance->mShutdownDone) {
+ aInstance->mMonitor.Wait();
+ }
+ }
+
+ delete aInstance;
+}
+
+void SandboxTestingParent::ActorDestroy(ActorDestroyReason aWhy) {
+ MOZ_RELEASE_ASSERT(mThread->IsOnThread());
+}
+
+mozilla::ipc::IPCResult SandboxTestingParent::RecvReportTestResults(
+ const nsCString& testName, bool shouldSucceed, bool didSucceed,
+ const nsCString& resultMessage) {
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("SandboxReportTestResults", [=]() {
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ MOZ_RELEASE_ASSERT(observerService);
+ const char* kFmt =
+ "{ \"testid\" : \"%s\", \"shouldPermit\" : %s, "
+ "\"wasPermitted\" : %s, \"message\" : \"%s\" }";
+ nsString json;
+ json.AppendPrintf(
+ kFmt, testName.BeginReading(), shouldSucceed ? "true" : "false",
+ didSucceed ? "true" : "false", resultMessage.BeginReading());
+ observerService->NotifyObservers(nullptr, "sandbox-test-result",
+ json.BeginReading());
+ }));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SandboxTestingParent::RecvTestCompleted() {
+ Unused << SendShutDown();
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("SandboxReportTestResults", []() {
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ MOZ_RELEASE_ASSERT(observerService);
+ observerService->NotifyObservers(nullptr, "sandbox-test-done", 0);
+ }));
+ return IPC_OK();
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/common/test/SandboxTestingParent.h b/security/sandbox/common/test/SandboxTestingParent.h
new file mode 100644
index 0000000000..5e7d1ab9ef
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTestingParent.h
@@ -0,0 +1,50 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxTestingParent_h
+#define mozilla_SandboxTestingParent_h
+
+#include "mozilla/PSandboxTestingParent.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/UniquePtr.h"
+
+#if !defined(MOZ_SANDBOX) || !defined(MOZ_DEBUG) || !defined(ENABLE_TESTS)
+# error "This file should not be used outside of debug with tests"
+#endif
+
+namespace mozilla {
+
+class SandboxTestingThread;
+
+class SandboxTestingParent : public PSandboxTestingParent {
+ public:
+ static SandboxTestingParent* Create(
+ Endpoint<PSandboxTestingParent>&& aParentEnd);
+ static void Destroy(SandboxTestingParent* aInstance);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ mozilla::ipc::IPCResult RecvReportTestResults(const nsCString& testName,
+ bool shouldSucceed,
+ bool didSucceed,
+ const nsCString& resultMessage);
+ mozilla::ipc::IPCResult RecvTestCompleted();
+
+ private:
+ explicit SandboxTestingParent(SandboxTestingThread* aThread,
+ Endpoint<PSandboxTestingParent>&& aParentEnd);
+ virtual ~SandboxTestingParent() = default;
+ void ShutdownSandboxTestThread();
+ void Bind(Endpoint<PSandboxTestingParent>&& aEnd);
+
+ UniquePtr<SandboxTestingThread> mThread;
+ Monitor mMonitor;
+ bool mShutdownDone;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxTestingParent_h
diff --git a/security/sandbox/common/test/SandboxTestingThread.h b/security/sandbox/common/test/SandboxTestingThread.h
new file mode 100644
index 0000000000..f0ddfa5573
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTestingThread.h
@@ -0,0 +1,53 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxTestingThread_h
+#define mozilla_SandboxTestingThread_h
+
+#include "nsThreadManager.h"
+
+#if !defined(MOZ_SANDBOX) || !defined(MOZ_DEBUG) || !defined(ENABLE_TESTS)
+# error "This file should not be used outside of debug with tests"
+#endif
+
+namespace mozilla {
+
+class SandboxTestingThread {
+ public:
+ void Dispatch(already_AddRefed<nsIRunnable>&& aRunnable) {
+ mThread->Dispatch(std::move(aRunnable), nsIEventTarget::NS_DISPATCH_NORMAL);
+ }
+
+ bool IsOnThread() {
+ bool on;
+ return NS_SUCCEEDED(mThread->IsOnCurrentThread(&on)) && on;
+ }
+
+ static SandboxTestingThread* Create() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIThread> thread;
+ if (NS_FAILED(
+ NS_NewNamedThread("Sandbox Testing", getter_AddRefs(thread)))) {
+ return nullptr;
+ }
+ return new SandboxTestingThread(thread);
+ }
+
+ ~SandboxTestingThread() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ mThread->Shutdown();
+ }
+
+ private:
+ explicit SandboxTestingThread(nsIThread* aThread) : mThread(aThread) {
+ MOZ_ASSERT(mThread);
+ }
+
+ nsCOMPtr<nsIThread> mThread;
+};
+} // namespace mozilla
+
+#endif // mozilla_SandboxTestingThread_h
diff --git a/security/sandbox/common/test/mozISandboxTest.idl b/security/sandbox/common/test/mozISandboxTest.idl
new file mode 100644
index 0000000000..7cde1defcf
--- /dev/null
+++ b/security/sandbox/common/test/mozISandboxTest.idl
@@ -0,0 +1,28 @@
+/* -*- Mode: IDL; 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 http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+// This interface is only for testing Sandbox.
+
+[scriptable, builtinclass, uuid(2306c118-3544-4674-9222-670b88dc07a9)]
+interface mozISandboxTest : nsISupports
+{
+ void startTests(in Array<ACString> aProcessesList);
+ void finishTests();
+};
+
+%{ C++
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+#define MOZ_SANDBOX_TEST_CID \
+ {0x989dda27, 0xb144, 0x45f9, {0x90, 0x39, 0x69, 0x74, 0x4e, 0xc6, dd0xd9, 0x12}}
+#define MOZ_SANDBOX_TEST_CONTRACTID \
+ "@mozilla.org/sandbox/sandbox-test;1"
+#else
+#error "This file should not be used outside of debug with tests"
+#endif
+%}