diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /security/sandbox/common | |
parent | Initial commit. (diff) | |
download | firefox-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.cpp | 94 | ||||
-rw-r--r-- | security/sandbox/common/SandboxSettings.h | 30 | ||||
-rw-r--r-- | security/sandbox/common/components.conf | 23 | ||||
-rw-r--r-- | security/sandbox/common/moz.build | 50 | ||||
-rw-r--r-- | security/sandbox/common/mozISandboxSettings.idl | 25 | ||||
-rw-r--r-- | security/sandbox/common/test/PSandboxTesting.ipdl | 18 | ||||
-rw-r--r-- | security/sandbox/common/test/SandboxTest.cpp | 112 | ||||
-rw-r--r-- | security/sandbox/common/test/SandboxTest.h | 33 | ||||
-rw-r--r-- | security/sandbox/common/test/SandboxTestingChild.cpp | 139 | ||||
-rw-r--r-- | security/sandbox/common/test/SandboxTestingChild.h | 66 | ||||
-rw-r--r-- | security/sandbox/common/test/SandboxTestingParent.cpp | 108 | ||||
-rw-r--r-- | security/sandbox/common/test/SandboxTestingParent.h | 50 | ||||
-rw-r--r-- | security/sandbox/common/test/SandboxTestingThread.h | 53 | ||||
-rw-r--r-- | security/sandbox/common/test/mozISandboxTest.idl | 28 |
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 +%} |