Backed out 2 changesets (bug 1827651) for causing TestAUSHelper related bustages...
[gecko.git] / ipc / glue / ForkServer.cpp
bloba50e442a828bbe1566a890c00873cdb454a9405d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ipc/ForkServer.h"
9 #include "chrome/common/chrome_switches.h"
10 #include "ipc/IPCMessageUtilsSpecializations.h"
11 #include "mozilla/BlockingResourceBase.h"
12 #include "mozilla/Logging.h"
13 #include "mozilla/Omnijar.h"
14 #include "mozilla/ProcessType.h"
15 #include "mozilla/ipc/FileDescriptor.h"
16 #include "mozilla/ipc/IPDLParamTraits.h"
17 #include "mozilla/ipc/ProtocolMessageUtils.h"
18 #include "mozilla/ipc/SetProcessTitle.h"
19 #include "nsTraceRefcnt.h"
21 #include <fcntl.h>
22 #include <string.h>
23 #include <unistd.h>
25 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
26 # include "mozilla/SandboxLaunch.h"
27 #endif
29 #include <algorithm>
31 namespace mozilla {
32 namespace ipc {
34 LazyLogModule gForkServiceLog("ForkService");
36 ForkServer::ForkServer() {}
38 /**
39 * Prepare an environment for running a fork server.
41 void ForkServer::InitProcess(int* aArgc, char*** aArgv) {
42 base::InitForkServerProcess();
44 mTcver = MakeUnique<MiniTransceiver>(kClientPipeFd,
45 DataBufferClear::AfterReceiving);
48 /**
49 * Preload any resources that the forked child processes might need,
50 * and which might change incompatibly or become unavailable by the
51 * time they're started. For example: the omnijar files, or certain
52 * shared libraries.
54 static void ForkServerPreload(int& aArgc, char** aArgv) {
55 Omnijar::ChildProcessInit(aArgc, aArgv);
58 /**
59 * Start providing the service at the IPC channel.
61 bool ForkServer::HandleMessages() {
62 while (true) {
63 UniquePtr<IPC::Message> msg;
64 if (!mTcver->Recv(msg)) {
65 break;
68 OnMessageReceived(std::move(msg));
70 if (mAppProcBuilder) {
71 // New process - child
72 return false;
75 // Stop the server
76 return true;
79 inline void CleanCString(nsCString& str) {
80 char* data;
81 int sz = str.GetMutableData(&data);
83 memset(data, ' ', sz);
86 inline void CleanString(std::string& str) {
87 const char deadbeef[] =
88 "\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef"
89 "\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
90 int pos = 0;
91 size_t sz = str.size();
92 while (sz > 0) {
93 int toclean = std::min(sz, sizeof(deadbeef) - 1);
94 str.replace(pos, toclean, deadbeef);
95 sz -= toclean;
96 pos += toclean;
100 inline void PrepareArguments(std::vector<std::string>& aArgv,
101 nsTArray<nsCString>& aArgvArray) {
102 for (auto& elt : aArgvArray) {
103 aArgv.push_back(elt.get());
104 CleanCString(elt);
108 // Prepare aOptions->env_map
109 inline void PrepareEnv(base::LaunchOptions* aOptions,
110 nsTArray<EnvVar>& aEnvMap) {
111 for (auto& elt : aEnvMap) {
112 nsCString& var = std::get<0>(elt);
113 nsCString& val = std::get<1>(elt);
114 aOptions->env_map[var.get()] = val.get();
115 CleanCString(var);
116 CleanCString(val);
120 // Prepare aOptions->fds_to_remap
121 inline void PrepareFdsRemap(base::LaunchOptions* aOptions,
122 nsTArray<FdMapping>& aFdsRemap) {
123 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, ("fds mapping:"));
124 for (auto& elt : aFdsRemap) {
125 // FDs are duplicated here.
126 int fd = std::get<0>(elt).ClonePlatformHandle().release();
127 std::pair<int, int> fdmap(fd, std::get<1>(elt));
128 aOptions->fds_to_remap.push_back(fdmap);
129 MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
130 ("\t%d => %d", fdmap.first, fdmap.second));
134 template <class P>
135 static void ReadParamInfallible(IPC::MessageReader* aReader, P* aResult,
136 const char* aCrashMessage) {
137 if (!IPC::ReadParam(aReader, aResult)) {
138 MOZ_CRASH_UNSAFE(aCrashMessage);
143 * Parse a Message to get a list of arguments and fill a LaunchOptions.
145 inline bool ParseForkNewSubprocess(IPC::Message& aMsg,
146 std::vector<std::string>& aArgv,
147 base::LaunchOptions* aOptions) {
148 if (aMsg.type() != Msg_ForkNewSubprocess__ID) {
149 MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
150 ("unknown message type %d\n", aMsg.type()));
151 return false;
154 IPC::MessageReader reader(aMsg);
155 nsTArray<nsCString> argv_array;
156 nsTArray<EnvVar> env_map;
157 nsTArray<FdMapping> fds_remap;
159 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
160 ReadParamInfallible(&reader, &aOptions->fork_flags,
161 "Error deserializing 'int'");
162 ReadParamInfallible(&reader, &aOptions->sandbox_chroot,
163 "Error deserializing 'bool'");
164 #endif
165 ReadParamInfallible(&reader, &argv_array,
166 "Error deserializing 'nsCString[]'");
167 ReadParamInfallible(&reader, &env_map, "Error deserializing 'EnvVar[]'");
168 ReadParamInfallible(&reader, &fds_remap, "Error deserializing 'FdMapping[]'");
169 reader.EndRead();
171 PrepareArguments(aArgv, argv_array);
172 PrepareEnv(aOptions, env_map);
173 PrepareFdsRemap(aOptions, fds_remap);
175 return true;
178 inline void SanitizeBuffers(IPC::Message& aMsg, std::vector<std::string>& aArgv,
179 base::LaunchOptions& aOptions) {
180 // Clean all buffers in the message to make sure content processes
181 // not peeking others.
182 auto& blist = aMsg.Buffers();
183 for (auto itr = blist.Iter(); !itr.Done();
184 itr.Advance(blist, itr.RemainingInSegment())) {
185 memset(itr.Data(), 0, itr.RemainingInSegment());
188 // clean all data string made from the message.
189 for (auto& var : aOptions.env_map) {
190 // Do it anyway since it is not going to be used anymore.
191 CleanString(*const_cast<std::string*>(&var.first));
192 CleanString(var.second);
194 for (auto& arg : aArgv) {
195 CleanString(arg);
200 * Extract parameters from the |Message| to create a
201 * |base::AppProcessBuilder| as |mAppProcBuilder|.
203 * It will return in both the fork server process and the new content
204 * process. |mAppProcBuilder| is null for the fork server.
206 void ForkServer::OnMessageReceived(UniquePtr<IPC::Message> message) {
207 std::vector<std::string> argv;
208 base::LaunchOptions options;
209 if (!ParseForkNewSubprocess(*message, argv, &options)) {
210 return;
213 base::ProcessHandle child_pid = -1;
214 mAppProcBuilder = MakeUnique<base::AppProcessBuilder>();
215 if (!mAppProcBuilder->ForkProcess(argv, std::move(options), &child_pid)) {
216 MOZ_CRASH("fail to fork");
218 MOZ_ASSERT(child_pid >= 0);
220 if (child_pid == 0) {
221 // Content process
222 return;
225 // Fork server process
227 mAppProcBuilder = nullptr;
229 IPC::Message reply(MSG_ROUTING_CONTROL, Reply_ForkNewSubprocess__ID);
230 IPC::MessageWriter writer(reply);
231 WriteIPDLParam(&writer, nullptr, child_pid);
232 mTcver->SendInfallible(reply, "failed to send a reply message");
234 // Without this, the content processes that is forked later are
235 // able to read the content of buffers even the buffers have been
236 // released.
237 SanitizeBuffers(*message, argv, options);
241 * Setup and run a fork server at the main thread.
243 * This function returns for two reasons:
244 * - the fork server is stopped normally, or
245 * - a new process is forked from the fork server and this function
246 * returned in the child, the new process.
248 * For the later case, aArgc and aArgv are modified to pass the
249 * arguments from the chrome process.
251 bool ForkServer::RunForkServer(int* aArgc, char*** aArgv) {
252 MOZ_ASSERT(XRE_IsForkServerProcess(), "fork server process only");
254 #ifdef DEBUG
255 if (getenv("MOZ_FORKSERVER_WAIT_GDB")) {
256 printf(
257 "Waiting for 30 seconds."
258 " Attach the fork server with gdb %s %d\n",
259 (*aArgv)[0], base::GetCurrentProcId());
260 sleep(30);
262 bool sleep_newproc = !!getenv("MOZ_FORKSERVER_WAIT_GDB_NEWPROC");
263 #endif
265 SetProcessTitleInit(*aArgv);
267 // Do this before NS_LogInit() to avoid log files taking lower
268 // FDs.
269 ForkServer forkserver;
270 forkserver.InitProcess(aArgc, aArgv);
272 NS_LogInit();
273 mozilla::LogModule::Init(0, nullptr);
274 ForkServerPreload(*aArgc, *aArgv);
275 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, ("Start a fork server"));
277 DebugOnly<base::ProcessHandle> forkserver_pid = base::GetCurrentProcId();
278 if (forkserver.HandleMessages()) {
279 // In the fork server process
280 // The server has stopped.
281 MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
282 ("Terminate the fork server"));
283 Omnijar::CleanUp();
284 NS_LogTerm();
285 return true;
287 // Now, we are running in a content process just forked from
288 // the fork server process.
289 MOZ_ASSERT(base::GetCurrentProcId() != forkserver_pid);
290 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, ("Fork a new content process"));
292 #ifdef DEBUG
293 if (sleep_newproc) {
294 printf(
295 "Waiting for 30 seconds."
296 " Attach the new process with gdb %s %d\n",
297 (*aArgv)[0], base::GetCurrentProcId());
298 sleep(30);
300 #endif
301 NS_LogTerm();
303 MOZ_ASSERT(forkserver.mAppProcBuilder);
305 // Bug 1909125: Refcount logging may be special FDs which are reserved
306 // for use when starting a child process. Make sure to close these files
307 // before the dup2 sequence in InitAppProcess to ensure they are not
308 // clobbered.
309 nsTraceRefcnt::CloseLogFilesAfterFork();
311 // |messageloop| has been destroyed. So, we can intialized the
312 // process safely. Message loops may allocates some file
313 // descriptors. If it is destroyed later, it may mess up this
314 // content process by closing wrong file descriptors.
315 forkserver.mAppProcBuilder->InitAppProcess(aArgc, aArgv);
316 forkserver.mAppProcBuilder.reset();
318 // Update our GeckoProcessType and GeckoChildID, removing the arguments.
319 if (*aArgc < 2) {
320 MOZ_CRASH("forked process missing process type and childid arguments");
322 SetGeckoProcessType((*aArgv)[--*aArgc]);
323 SetGeckoChildID((*aArgv)[--*aArgc]);
324 MOZ_ASSERT(!XRE_IsForkServerProcess(),
325 "fork server created another fork server?");
327 // Open log files again with right names and the new PID.
328 nsTraceRefcnt::ReopenLogFilesAfterFork(XRE_GetProcessTypeString());
330 return false;
333 } // namespace ipc
334 } // namespace mozilla