Bug 1908725 - Part 5: Add additional validation for early-startup command line checks, r=ipc-reviewers,mccr8

This patch adjusts the various places where we initialize content
processes to call SetGeckoProcessType as early as possible, and be more
consistent. After this change we should only ever set GeckoProcessType
and GeckoChildID once per-process (with the exception of the fork server
process).

In addition to this validation, some more checks around the fork server
were added, such as to prevent forking another forkserver, or forking a
non-content process.

As part of this change, there was some refactoring/cleanup done, such as
removing plugin-container.cpp and content_process_main, as compared to
the other duplicated code between the two call-sites, the duplication
was relatively small, and inlining it helped make things more readable.

Differential Revision: https://phabricator.services.mozilla.com/D218471
This commit is contained in:
Nika Layzell
2024-08-07 20:39:41 +00:00
parent 112fcb23c7
commit 1a2a376d39
6 changed files with 160 additions and 187 deletions

View File

@@ -5,6 +5,7 @@
#include "nsXULAppAPI.h"
#include "mozilla/XREAppData.h"
#include "XREChildData.h"
#include "XREShellData.h"
#include "application.ini.h"
#include "mozilla/Bootstrap.h"
@@ -38,9 +39,12 @@
# include "mozilla/WindowsProcessMitigations.h"
# define XRE_WANT_ENVIRON
# include "nsWindowsWMain.cpp"
# define strcasecmp _stricmp
# ifdef MOZ_SANDBOX
# include "mozilla/sandboxing/SandboxInitialization.h"
# include "mozilla/sandboxing/sandboxLogging.h"
# endif
#endif
#include "BinaryPath.h"
@@ -97,7 +101,6 @@ __attribute__((constructor)) static void SSE2Check() {
#if !defined(MOZ_WIDGET_COCOA) && !defined(MOZ_WIDGET_ANDROID)
# define MOZ_BROWSER_CAN_BE_CONTENTPROC
# include "../../ipc/contentproc/plugin-container.cpp"
#endif
using namespace mozilla;
@@ -280,30 +283,38 @@ int main(int argc, char* argv[], char* envp[]) {
#if defined(XP_UNIX)
ReserveDefaultFileDescriptors();
#endif
#if defined(MOZ_ENABLE_FORKSERVER)
if (strcmp(argv[argc - 1], "forkserver") == 0) {
nsresult rv = InitXPCOMGlue(LibLoadingStrategy::NoReadAhead);
if (NS_FAILED(rv)) {
return 255;
}
// Run a fork server in this process, single thread. When it
// returns, it means the fork server have been stopped or a new
// content process is created.
//
// For the later case, XRE_ForkServer() will return false, running
// in a content process just forked from the fork server process.
// argc & argv will be updated with the values passing from the
// chrome process. With the new values, this function
// continues the reset of the code acting as a content process.
if (gBootstrap->XRE_ForkServer(&argc, &argv)) {
// Return from the fork server in the fork server process.
// Stop the fork server.
gBootstrap->NS_LogTerm();
return 0;
#ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
if (argc > 1 && IsArg(argv[1], "contentproc")) {
// Set the process type and gecko child id.
SetGeckoProcessType(argv[--argc]);
SetGeckoChildID(argv[--argc]);
# if defined(MOZ_ENABLE_FORKSERVER)
if (GetGeckoProcessType() == GeckoProcessType_ForkServer) {
nsresult rv = InitXPCOMGlue(LibLoadingStrategy::NoReadAhead);
if (NS_FAILED(rv)) {
return 255;
}
// Run a fork server in this process, single thread. When it returns, it
// means the fork server have been stopped or a new child process is
// created.
//
// For the latter case, XRE_ForkServer() will return false, running in a
// child process just forked from the fork server process. argc & argv
// will be updated with the values passing from the chrome process, as
// will GeckoProcessType and GeckoChildID. With the new values, this
// function continues the reset of the code acting as a child process.
if (gBootstrap->XRE_ForkServer(&argc, &argv)) {
// Return from the fork server in the fork server process.
// Stop the fork server.
// InitXPCOMGlue calls NS_LogInit, so we need to balance it here.
gBootstrap->NS_LogTerm();
return 0;
}
}
// In a content process forked from the fork server.
// Start acting as a content process.
# endif
}
#endif
@@ -312,25 +323,19 @@ int main(int argc, char* argv[], char* envp[]) {
AUTO_BASE_PROFILER_INIT;
AUTO_BASE_PROFILER_LABEL("nsBrowserApp main", OTHER);
// Register an external module to report on otherwise uncatchable exceptions.
// Note that in child processes this must be called after Gecko process type
// has been set.
CrashReporter::RegisterRuntimeExceptionModule();
// Make sure we unregister the runtime exception module before returning.
// We do this here to cover both registers for child and main processes.
auto unregisterRuntimeExceptionModule =
MakeScopeExit([] { CrashReporter::UnregisterRuntimeExceptionModule(); });
#ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
// We are launching as a content process, delegate to the appropriate
// main
if (argc > 1 && IsArg(argv[1], "contentproc")) {
// Set the process type and gecko child id. We don't remove the args here as
// that will be done later in common code.
SetGeckoProcessType(argv[argc - 1]);
SetGeckoChildID(argv[argc - 2]);
// Register an external module to report on otherwise uncatchable
// exceptions. Note that in child processes this must be called after Gecko
// process type has been set.
CrashReporter::RegisterRuntimeExceptionModule();
if (GetGeckoProcessType() != GeckoProcessType_Default) {
# if defined(XP_WIN) && defined(MOZ_SANDBOX)
// We need to set whether our process is supposed to have win32k locked down
// from the command line setting before DllBlocklist_Initialize,
@@ -376,7 +381,26 @@ int main(int argc, char* argv[], char* envp[]) {
return 255;
}
int result = content_process_main(gBootstrap.get(), argc, argv);
XREChildData childData;
# if defined(XP_WIN) && defined(MOZ_SANDBOX)
if (IsSandboxedProcess()) {
childData.sandboxTargetServices =
mozilla::sandboxing::GetInitializedTargetServices();
if (!childData.sandboxTargetServices) {
return 1;
}
childData.ProvideLogFunction = mozilla::sandboxing::ProvideLogFunction;
}
if (GetGeckoProcessType() == GeckoProcessType_RemoteSandboxBroker) {
childData.sandboxBrokerServices =
mozilla::sandboxing::GetInitializedBrokerServices();
}
# endif
rv = gBootstrap->XRE_InitChildProcess(argc, argv, &childData);
# if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
DllBlocklist_Shutdown();
@@ -385,13 +409,10 @@ int main(int argc, char* argv[], char* envp[]) {
// InitXPCOMGlue calls NS_LogInit, so we need to balance it here.
gBootstrap->NS_LogTerm();
return result;
return NS_FAILED(rv) ? 1 : 0;
}
#endif
// Register an external module to report on otherwise uncatchable exceptions.
CrashReporter::RegisterRuntimeExceptionModule();
#ifdef HAS_DLL_BLOCKLIST
DllBlocklist_Initialize(gBlocklistInitFlags);
#endif

View File

@@ -4,59 +4,34 @@
* 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 "../contentproc/plugin-container.cpp"
#include "nsXULAppAPI.h"
#include "XREChildData.h"
#include "mozilla/Bootstrap.h"
#include "mozilla/ProcessType.h"
#include "mozilla/RuntimeExceptionModule.h"
#include "mozilla/ScopeExit.h"
#if defined(XP_WIN)
# include "mozilla/WindowsDllBlocklist.h"
# include "mozilla/GeckoArgs.h"
# include "nsWindowsWMain.cpp"
# ifdef MOZ_SANDBOX
# include "mozilla/sandboxing/SandboxInitialization.h"
# include "mozilla/sandboxing/sandboxLogging.h"
# endif
#endif // defined(XP_WIN)
using namespace mozilla;
static bool UseForkServer(int argc, char* argv[]) {
#if defined(MOZ_ENABLE_FORKSERVER)
return strcmp(argv[argc - 1], "forkserver") == 0;
#else
return false;
#endif
}
static int RunForkServer(Bootstrap::UniquePtr&& bootstrap, int argc,
char* argv[]) {
#if defined(MOZ_ENABLE_FORKSERVER)
int ret = 0;
bootstrap->NS_LogInit();
// Run a fork server in this process, single thread. When it
// returns, it means the fork server have been stopped or a new
// content process is created.
//
// For the later case, XRE_ForkServer() will return false, running
// in a content process just forked from the fork server process.
// argc & argv will be updated with the values passing from the
// chrome process. With the new values, this function
// continues the reset of the code acting as a content process.
if (bootstrap->XRE_ForkServer(&argc, &argv)) {
// Return from the fork server in the fork server process.
// Stop the fork server.
} else {
// In a content process forked from the fork server.
// Start acting as a content process.
ret = content_process_main(bootstrap.get(), argc, argv);
}
bootstrap->NS_LogTerm();
return ret;
#else
return 0;
#endif
}
int main(int argc, char* argv[]) {
// Set the process type and gecko child id.
if (argc < 2) {
return 3;
}
SetGeckoProcessType(argv[--argc]);
SetGeckoChildID(argv[--argc]);
auto bootstrapResult = GetBootstrap();
if (bootstrapResult.isErr()) {
return 2;
@@ -64,36 +39,67 @@ int main(int argc, char* argv[]) {
Bootstrap::UniquePtr bootstrap = bootstrapResult.unwrap();
int ret;
if (UseForkServer(argc, argv)) {
ret = RunForkServer(std::move(bootstrap), argc, argv);
} else {
// Set the process type and gecko child id. We don't remove the args here as
// that will be done later in common code.
SetGeckoProcessType(argv[argc - 1]);
SetGeckoChildID(argv[argc - 2]);
#if defined(MOZ_ENABLE_FORKSERVER)
if (GetGeckoProcessType() == GeckoProcessType_ForkServer) {
bootstrap->NS_LogInit();
// Register an external module to report on otherwise uncatchable
// exceptions. Note that in child processes this must be called after Gecko
// process type has been set.
CrashReporter::RegisterRuntimeExceptionModule();
// Run a fork server in this process, single thread. When it returns, it
// means the fork server have been stopped or a new child process is
// created.
//
// For the latter case, XRE_ForkServer() will return false, running in a
// child process just forked from the fork server process. argc & argv will
// be updated with the values passing from the chrome process, as will
// GeckoProcessType and GeckoChildID. With the new values, this function
// continues the reset of the code acting as a child process.
if (bootstrap->XRE_ForkServer(&argc, &argv)) {
// Return from the fork server in the fork server process.
// Stop the fork server.
bootstrap->NS_LogTerm();
return 0;
}
}
#endif
// Make sure we unregister the runtime exception module before returning.
auto unregisterRuntimeExceptionModule = MakeScopeExit(
[] { CrashReporter::UnregisterRuntimeExceptionModule(); });
// Register an external module to report on otherwise uncatchable
// exceptions. Note that in child processes this must be called after Gecko
// process type has been set.
CrashReporter::RegisterRuntimeExceptionModule();
// Make sure we unregister the runtime exception module before returning.
auto unregisterRuntimeExceptionModule =
MakeScopeExit([] { CrashReporter::UnregisterRuntimeExceptionModule(); });
#ifdef HAS_DLL_BLOCKLIST
uint32_t initFlags = eDllBlocklistInitFlagIsChildProcess;
SetDllBlocklistProcessTypeFlags(initFlags, GetGeckoProcessType());
DllBlocklist_Initialize(initFlags);
uint32_t initFlags = eDllBlocklistInitFlagIsChildProcess;
SetDllBlocklistProcessTypeFlags(initFlags, GetGeckoProcessType());
DllBlocklist_Initialize(initFlags);
#endif
ret = content_process_main(bootstrap.get(), argc, argv);
XREChildData childData;
#if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
DllBlocklist_Shutdown();
#endif
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
if (IsSandboxedProcess()) {
childData.sandboxTargetServices =
mozilla::sandboxing::GetInitializedTargetServices();
if (!childData.sandboxTargetServices) {
return 1;
}
childData.ProvideLogFunction = mozilla::sandboxing::ProvideLogFunction;
}
return ret;
if (GetGeckoProcessType() == GeckoProcessType_RemoteSandboxBroker) {
childData.sandboxBrokerServices =
mozilla::sandboxing::GetInitializedBrokerServices();
}
#endif
nsresult rv = bootstrap->XRE_InitChildProcess(argc, argv, &childData);
#if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
DllBlocklist_Shutdown();
#endif
return NS_FAILED(rv) ? 1 : 0;
}

View File

@@ -1,62 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=4 et :
* 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 "nsXPCOM.h"
#include "nsXULAppAPI.h"
#include "mozilla/Bootstrap.h"
#include "mozilla/ProcessType.h"
#include "XREChildData.h"
#ifdef XP_WIN
# include <windows.h>
// we want a wmain entry point
# include "nsWindowsWMain.cpp"
#else
// FIXME/cjones testing
# include <unistd.h>
#endif
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
# include "mozilla/sandboxing/SandboxInitialization.h"
# include "mozilla/sandboxing/sandboxLogging.h"
#endif
int content_process_main(mozilla::Bootstrap* bootstrap, int argc,
char* argv[]) {
// Check for the absolute minimum number of args we need to move
// forward here. We expect the last arg to be the child process type, and the
// second-last argument to be the gecko child id.
if (argc < 2) {
return 3;
}
XREChildData childData;
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
if (IsSandboxedProcess()) {
childData.sandboxTargetServices =
mozilla::sandboxing::GetInitializedTargetServices();
if (!childData.sandboxTargetServices) {
return 1;
}
childData.ProvideLogFunction = mozilla::sandboxing::ProvideLogFunction;
}
#endif
mozilla::SetGeckoProcessType(argv[--argc]);
mozilla::SetGeckoChildID(argv[--argc]);
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
if (mozilla::GetGeckoProcessType() == GeckoProcessType_RemoteSandboxBroker) {
childData.sandboxBrokerServices =
mozilla::sandboxing::GetInitializedBrokerServices();
}
#endif
nsresult rv = bootstrap->XRE_InitChildProcess(argc, argv, &childData);
return NS_FAILED(rv);
}

View File

@@ -249,6 +249,8 @@ void ForkServer::OnMessageReceived(UniquePtr<IPC::Message> message) {
* arguments from the chrome process.
*/
bool ForkServer::RunForkServer(int* aArgc, char*** aArgv) {
MOZ_ASSERT(XRE_IsForkServerProcess(), "fork server process only");
#ifdef DEBUG
if (getenv("MOZ_FORKSERVER_WAIT_GDB")) {
printf(
@@ -262,12 +264,6 @@ bool ForkServer::RunForkServer(int* aArgc, char*** aArgv) {
SetProcessTitleInit(*aArgv);
// The last two arguments are the GeckoChildID and "forkserver".
MOZ_ASSERT(!strcmp((*aArgv)[*aArgc - 1], "forkserver"),
"last argument is not \"forkserver\"");
SetGeckoProcessType("forkserver");
SetGeckoChildID((*aArgv)[*aArgc - 2]);
// Do this before NS_LogInit() to avoid log files taking lower
// FDs.
ForkServer forkserver;
@@ -319,8 +315,17 @@ bool ForkServer::RunForkServer(int* aArgc, char*** aArgv) {
forkserver.mAppProcBuilder->InitAppProcess(aArgc, aArgv);
forkserver.mAppProcBuilder.reset();
// Update our GeckoProcessType and GeckoChildID, removing the arguments.
if (*aArgc < 2) {
MOZ_CRASH("forked process missing process type and childid arguments");
}
SetGeckoProcessType((*aArgv)[--*aArgc]);
SetGeckoChildID((*aArgv)[--*aArgc]);
MOZ_ASSERT(!XRE_IsForkServerProcess(),
"fork server created another fork server?");
// Open log files again with right names and the new PID.
nsTraceRefcnt::ReopenLogFilesAfterFork((*aArgv)[*aArgc - 1]);
nsTraceRefcnt::ReopenLogFilesAfterFork(XRE_GetProcessTypeString());
return false;
}

View File

@@ -393,6 +393,11 @@ Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(
ElfLoader::Singleton.ExpectShutdown(true);
#endif
} else {
if (argc < 2) {
FreeArgv(argv, argc);
return;
}
SetGeckoProcessType(argv[--argc]);
SetGeckoChildID(argv[--argc]);
@@ -416,6 +421,12 @@ extern "C" APKOPEN_EXPORT mozglueresult ChildProcessInit(int argc,
char* argv[]) {
EnsureBaseProfilerInitialized();
if (argc < 2) {
return FAILURE;
}
SetGeckoProcessType(argv[--argc]);
SetGeckoChildID(argv[--argc]);
if (loadNSSLibs() != SUCCESS) {
return FAILURE;
}
@@ -426,9 +437,6 @@ extern "C" APKOPEN_EXPORT mozglueresult ChildProcessInit(int argc,
return FAILURE;
}
SetGeckoProcessType(argv[--argc]);
SetGeckoChildID(argv[--argc]);
XREChildData childData;
return NS_FAILED(gBootstrap->XRE_InitChildProcess(argc, argv, &childData));
}

View File

@@ -19,24 +19,15 @@ GeckoChildID sGeckoChildID = 0;
} // namespace startup
void SetGeckoProcessType(const char* aProcessTypeString) {
#if !defined(DEBUG)
// If not a DEBUG build then just return if sChildProcessType has already been
// set and is not fork server. In DEBUG builds we will check that process type
// matches the one already set if it is not fork server.
if (sChildProcessType != GeckoProcessType_Default &&
sChildProcessType != GeckoProcessType_ForkServer) {
return;
MOZ_CRASH("Cannot set GeckoProcessType multiple times.");
}
#endif
#define GECKO_PROCESS_TYPE(enum_value, enum_name, string_name, proc_typename, \
process_bin_type, procinfo_typename, \
webidl_typename, allcaps_name) \
if (std::strcmp(aProcessTypeString, string_name) == 0) { \
MOZ_ASSERT_IF( \
sChildProcessType != GeckoProcessType_Default && \
sChildProcessType != GeckoProcessType_ForkServer, \
sChildProcessType == GeckoProcessType::GeckoProcessType_##enum_name); \
sChildProcessType = GeckoProcessType::GeckoProcessType_##enum_name; \
return; \
}
@@ -58,6 +49,10 @@ void SetGeckoProcessType(const char* aProcessTypeString) {
void SetGeckoChildID(const char* aGeckoChildIDString) {
sGeckoChildID = atoi(aGeckoChildIDString);
if (sGeckoChildID <= 0) {
MOZ_CRASH("aGeckoChildIDString is not valid.");
}
}
} // namespace mozilla