Bug 1710625 - SandboxTest with SocketProcess r=necko-reviewers,handyman,jld,kershaw

Differential Revision: https://phabricator.services.mozilla.com/D114861
This commit is contained in:
Alexandre Lissy 2021-06-03 06:45:59 +00:00
parent 07973852a6
commit 6b5f586e94
12 changed files with 219 additions and 45 deletions

View File

@ -21,6 +21,10 @@ include protocol PProxyConfigLookup;
include protocol PNativeDNSResolverOverride;
include protocol PRemoteLazyInputStream;
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
include protocol PSandboxTesting;
#endif
include MemoryReportTypes;
include NeckoChannelParams;
include PrefsTypes;
@ -143,6 +147,9 @@ child:
async InitLinuxSandbox(FileDescriptor? sandboxBroker);
async InitSocketProcessBridgeParent(ProcessId processId, Endpoint<PSocketProcessBridgeParent> endpoint);
async InitProfiler(Endpoint<PProfilerChild> aEndpoint);
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
async InitSandboxTesting(Endpoint<PSandboxTestingChild> aEndpoint);
#endif
// test-only
async SocketProcessTelemetryPing();

View File

@ -61,6 +61,10 @@
# include "mozilla/net/WebrtcTCPSocketChild.h"
#endif
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
# include "mozilla/SandboxTestingChild.h"
#endif
namespace mozilla {
namespace net {
@ -275,6 +279,17 @@ mozilla::ipc::IPCResult SocketProcessChild::RecvInitProfiler(
return IPC_OK();
}
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
mozilla::ipc::IPCResult SocketProcessChild::RecvInitSandboxTesting(
Endpoint<PSandboxTestingChild>&& aEndpoint) {
if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) {
return IPC_FAIL(
this, "InitSandboxTesting failed to initialise the child process.");
}
return IPC_OK();
}
#endif
mozilla::ipc::IPCResult SocketProcessChild::RecvSocketProcessTelemetryPing() {
const uint32_t kExpectedUintValue = 42;
Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_SOCKET_ONLY_UINT,

View File

@ -56,6 +56,10 @@ class SocketProcessChild final
Endpoint<mozilla::net::PSocketProcessBridgeParent>&& aEndpoint);
mozilla::ipc::IPCResult RecvInitProfiler(
Endpoint<mozilla::PProfilerChild>&& aEndpoint);
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
mozilla::ipc::IPCResult RecvInitSandboxTesting(
Endpoint<PSandboxTestingChild>&& aEndpoint);
#endif
mozilla::ipc::IPCResult RecvSocketProcessTelemetryPing();
PWebrtcTCPSocketChild* AllocPWebrtcTCPSocketChild(const Maybe<TabId>& tabId);

View File

@ -56,6 +56,7 @@ UNIFIED_SOURCES += [
PREPROCESSED_IPDL_SOURCES += [
"PNecko.ipdl",
"PSocketProcess.ipdl",
]
IPDL_SOURCES = [
@ -66,7 +67,6 @@ IPDL_SOURCES = [
"PInputChannelThrottleQueue.ipdl",
"PProxyConfigLookup.ipdl",
"PSimpleChannel.ipdl",
"PSocketProcess.ipdl",
"PSocketProcessBridge.ipdl",
]

View File

@ -41,6 +41,10 @@ if CONFIG["MOZ_SANDBOX"] and CONFIG["MOZ_DEBUG"] and CONFIG["ENABLE_TESTS"]:
"test/mozISandboxTest.idl",
]
LOCAL_INCLUDES += [
"/netwerk/base",
]
include("/ipc/chromium/chromium-config.mozbuild")
FINAL_LIBRARY = "xul"

View File

@ -7,12 +7,15 @@
#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/ipc/Endpoint.h"
#include "nsIOService.h"
using namespace mozilla;
using namespace mozilla::ipc;
@ -84,6 +87,40 @@ SandboxTest::StartTests(const nsTArray<nsCString>& aProcessesList) {
break;
}
case GeckoProcessType_Socket: {
// mochitest harness force this variable, but we actually do not want
// that
int rv_unset =
#ifdef XP_UNIX
unsetenv("MOZ_DISABLE_SOCKET_PROCESS");
#endif // XP_UNIX
#ifdef XP_WIN
_putenv("MOZ_DISABLE_SOCKET_PROCESS=");
#endif // XP_WIN
MOZ_ASSERT(rv_unset == 0, "Error unsetting env var");
nsresult rv_pref =
Preferences::SetBool("network.process.enabled", true);
MOZ_ASSERT(rv_pref == NS_OK, "Error enforcing pref");
MOZ_ASSERT(net::gIOService, "No gIOService?");
RefPtr<SandboxTest> self = this;
net::gIOService->CallOrWaitForSocketProcess([self, type]() {
// 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
//
// If that happens, then by the time we reach the end of StartTests()
// the mSandboxTestingParents[type] might not yet have been updated
// this is why below we allow for a delayed dispatch to give a chance
net::SocketProcessParent* parent =
net::SocketProcessParent::GetSingleton();
self->mSandboxTestingParents[type] =
InitializeSandboxTestingActors(parent);
});
break;
}
default:
MOZ_ASSERT_UNREACHABLE(
"SandboxTest does not yet support this process type");
@ -91,7 +128,24 @@ SandboxTest::StartTests(const nsTArray<nsCString>& aProcessesList) {
}
if (!mSandboxTestingParents[type]) {
return NS_ERROR_FAILURE;
if (type == GeckoProcessType_Socket) {
// Give a chance to the socket process to be present and to the callback
// above to be ran. We delay by 5 seconds, but hopefully since it is all
// on the main thread, the value itself should not be important.
RefPtr<SandboxTest> self = this;
NS_DelayedDispatchToCurrentThread(
NS_NewRunnableFunction(
"SandboxTest::StartTests",
[self, type]() {
if (!self->mSandboxTestingParents[type]) {
MOZ_ASSERT_UNREACHABLE(
"SandboxTest failed to get a Parent");
}
}),
5e3 /* delay by five seconds */);
} else {
return NS_ERROR_FAILURE;
}
}
}
return NS_OK;

View File

@ -5,18 +5,15 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "SandboxTestingChild.h"
#include "SandboxTestingChildTests.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>
#ifdef XP_LINUX
# include "mozilla/Sandbox.h"
#endif
#include "nsXULAppAPI.h"
namespace mozilla {
SandboxTestingChild* SandboxTestingChild::sInstance = nullptr;
@ -61,37 +58,22 @@ void SandboxTestingChild::Bind(Endpoint<PSandboxTestingChild>&& aEndpoint) {
DebugOnly<bool> ok = aEndpoint.Bind(this);
MOZ_ASSERT(ok);
#ifdef XP_LINUX
bool sandboxCrashOnError = SetSandboxCrashOnError(false);
#endif
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
RunTestsContent(this);
}
if (XRE_IsSocketProcess()) {
RunTestsSocket(this);
}
#ifdef XP_LINUX
SetSandboxCrashOnError(sandboxCrashOnError);
#endif
// Tell SandboxTest that this process is done with all tests.
SendTestCompleted();
}

View File

@ -40,11 +40,6 @@ class SandboxTestingChild : public PSandboxTestingChild {
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);
@ -56,6 +51,11 @@ class SandboxTestingChild : public PSandboxTestingChild {
void ErrnoTest(const nsCString& aName, bool aExpectSuccess, F&& aFunction);
#endif
private:
explicit SandboxTestingChild(SandboxTestingThread* aThread,
Endpoint<PSandboxTestingChild>&& aEndpoint);
void Bind(Endpoint<PSandboxTestingChild>&& aEndpoint);
UniquePtr<SandboxTestingThread> mThread;
static SandboxTestingChild* sInstance;

View File

@ -0,0 +1,95 @@
/* -*- 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 "nsXULAppAPI.h"
#ifdef XP_UNIX
# include <fcntl.h>
# include <netdb.h>
# ifdef XP_LINUX
# include <sys/prctl.h>
# endif // XP_LINUX
# include <sys/socket.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <time.h>
# include <unistd.h>
#endif
namespace mozilla {
void RunTestsContent(SandboxTestingChild* child) {
MOZ_ASSERT(child, "No SandboxTestingChild*?");
#ifdef XP_UNIX
struct stat st;
static const char kAllowedPath[] = "/usr/lib";
child->ErrnoTest("fstatat_as_stat"_ns, true,
[&] { return fstatat(AT_FDCWD, kAllowedPath, &st, 0); });
child->ErrnoTest("fstatat_as_lstat"_ns, true, [&] {
return fstatat(AT_FDCWD, kAllowedPath, &st, AT_SYMLINK_NOFOLLOW);
});
# ifdef XP_LINUX
child->ErrnoTest("fstatat_as_fstat"_ns, true,
[&] { return fstatat(0, "", &st, AT_EMPTY_PATH); });
# endif // XP_LINUX
const struct timespec usec = {0, 1000};
child->ErrnoTest("nanosleep"_ns, true,
[&] { return nanosleep(&usec, nullptr); });
struct timespec res = {0, 0};
child->ErrnoTest("clock_getres"_ns, true,
[&] { return clock_getres(CLOCK_REALTIME, &res); });
#else // XP_UNIX
child->SendReportTestResults(
"dummy_test"_ns,
/* shouldSucceed */ true,
/* didSucceed */ true,
"The test framework fails if there are no cases."_ns);
#endif // XP_UNIX
}
void RunTestsSocket(SandboxTestingChild* child) {
MOZ_ASSERT(child, "No SandboxTestingChild*?");
#ifdef XP_UNIX
child->ErrnoTest("getaddrinfo"_ns, true, [&] {
struct addrinfo* res;
int rv = getaddrinfo("localhost", nullptr, nullptr, &res);
if (res != nullptr) {
freeaddrinfo(res);
}
return rv;
});
# ifdef XP_LINUX
child->ErrnoTest("prctl_allowed"_ns, true, [&] {
int rv = prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
return rv;
});
child->ErrnoTest("prctl_blocked"_ns, false, [&] {
int rv = prctl(PR_GET_SECCOMP, 0, 0, 0, 0);
return rv;
});
# endif // XP_LINUX
#else // XP_UNIX
child->SendReportTestResults(
"dummy_test"_ns,
/* shouldSucceed */ true,
/* didSucceed */ true,
"The test framework fails if there are no cases."_ns);
#endif // XP_UNIX
}
} // namespace mozilla

View File

@ -83,7 +83,7 @@ mozilla::Atomic<int> gSeccompTsyncBroadcastSignum(0);
namespace mozilla {
static bool gSandboxCrashOnError = false;
static mozilla::Atomic<bool> gSandboxCrashOnError(false);
// This is initialized by SandboxSetCrashFunc().
SandboxCrashFunc gSandboxCrashFunc;
@ -709,4 +709,10 @@ void SetSocketProcessSandbox(int aBroker) {
SetCurrentProcessSandbox(GetSocketProcessSandboxPolicy(sBroker));
}
bool SetSandboxCrashOnError(bool aValue) {
bool oldValue = gSandboxCrashOnError;
gSandboxCrashOnError = aValue;
return oldValue;
}
} // namespace mozilla

View File

@ -63,6 +63,10 @@ MOZ_EXPORT void SetRemoteDataDecoderSandbox(int aBroker);
MOZ_EXPORT void SetSocketProcessSandbox(int aBroker);
// We want to turn on/off crashing on error when running some tests
// This will return current value and set the aValue we pass
MOZ_EXPORT bool SetSandboxCrashOnError(bool aValue);
} // namespace mozilla
#endif // mozilla_Sandbox_h

View File

@ -11,7 +11,10 @@ function test() {
);
// Types of processes to test, taken from GeckoProcessTypes.h
var processTypes = ["tab", "gpu"];
// GPU process might not run depending on the platform, so we need it to be
// the last one of the list to allow the remainingTests logic below to work
// as expected.
var processTypes = ["tab", "socket", "gpu"];
// A callback called after each test-result.
Services.obs.addObserver(function result(subject, topic, data) {