gecko-dev/ipc/glue/ForkServiceChild.h
Jed Davis 48864d54a3 Bug 1880590 - Fix benign data race during fork server shutdown. r=nika
There's a race between `ProcessWatcher` checking if the fork server is
in use (via `ForkServiceChild::Get`) and the fork server shutdown path
(`ForkServerLauncher::Observe` calling `StopForkServer`).  The race
seems to be benign, but it causes test failures under TSan.

As a relatively simple fix, this patch changes `ProcessWatcher` to check
an atomic flag which is true if the fork server has ever been run.
(This also means that, in the case where the fork server has been shut
down but some of its child processes are still running, we will continue
to use the `kill(pid, 0)` fallback.)

Differential Revision: https://phabricator.services.mozilla.com/D203077
2024-03-02 01:44:38 +00:00

120 lines
3.3 KiB
C++

/* -*- 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/. */
#ifndef __FORKSERVICE_CHILD_H_
#define __FORKSERVICE_CHILD_H_
#include "base/process_util.h"
#include "nsIObserver.h"
#include "nsString.h"
#include "mozilla/ipc/MiniTransceiver.h"
#include "mozilla/ipc/LaunchError.h"
#include "mozilla/Result.h"
#include <sys/types.h>
#include <poll.h>
namespace mozilla {
namespace ipc {
class GeckoChildProcessHost;
/**
* This is the interface to the fork server.
*
* When the chrome process calls |ForkServiceChild| to create a new
* process, this class send a message to the fork server through a
* pipe and get the PID of the new process from the reply.
*/
class ForkServiceChild {
public:
ForkServiceChild(int aFd, GeckoChildProcessHost* aProcess);
virtual ~ForkServiceChild();
struct Args {
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
int mForkFlags = 0;
bool mChroot = false;
#endif
nsTArray<nsCString> mArgv;
nsTArray<EnvVar> mEnv;
nsTArray<FdMapping> mFdsRemap;
};
/**
* Ask the fork server to create a new process with given parameters.
*
* The fork server uses |base::LaunchApp()| to create a new
* content process with the following parameters.
*
* \param aArgv assigns |argv| of the content process.
* \param aEnvMap sets |LaunchOptions::env_map|.
* \param aFdsRemap sets |LaunchOptions::fd_to_remap|.
* \param aPid returns the PID of the content process created.
* \return true if success.
*/
Result<Ok, LaunchError> SendForkNewSubprocess(const Args& aArgs, pid_t* aPid);
/**
* Create a fork server process and the singleton of this class.
*
* This function uses |GeckoChildProcessHost| to launch the fork
* server, getting the fd of a pipe/socket to the fork server from
* it's |IPC::Channel|.
*/
static void StartForkServer();
static void StopForkServer();
/**
* Return the singleton.
*/
static ForkServiceChild* Get() {
auto child = sForkServiceChild.get();
return child == nullptr || child->mFailed ? nullptr : child;
}
/**
* Returns whether the fork server was ever active. Thread-safe.
*/
static bool WasUsed() { return sForkServiceUsed; }
private:
// Called when a message is received.
void OnMessageReceived(UniquePtr<IPC::Message> message);
void OnError();
UniquePtr<MiniTransceiver> mTcver;
static UniquePtr<ForkServiceChild> sForkServiceChild;
static Atomic<bool> sForkServiceUsed;
pid_t mRecvPid;
bool mFailed; // The forkserver has crashed or disconnected.
GeckoChildProcessHost* mProcess;
};
/**
* Start a fork server at |xpcom-startup| from the chrome process.
*/
class ForkServerLauncher : public nsIObserver {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
ForkServerLauncher();
static already_AddRefed<ForkServerLauncher> Create();
private:
friend class ForkServiceChild;
virtual ~ForkServerLauncher();
static void RestartForkServer();
static bool mHaveStartedClient;
static StaticRefPtr<ForkServerLauncher> mSingleton;
};
} // namespace ipc
} // namespace mozilla
#endif /* __FORKSERVICE_CHILD_H_ */