Backed out 8 changesets (bug 1873776) for causing vendor failures. CLOSED TREE
[gecko.git] / ipc / glue / ForkServer.cpp
blobb70fdbf2c003650668040b620dc33dceecfcc5fe
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/ipc/FileDescriptor.h"
15 #include "mozilla/ipc/IPDLParamTraits.h"
16 #include "mozilla/ipc/ProtocolMessageUtils.h"
17 #include "mozilla/ipc/SetProcessTitle.h"
18 #include "nsTraceRefcnt.h"
20 #include <fcntl.h>
21 #include <string.h>
22 #include <unistd.h>
24 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
25 # include "mozilla/SandboxLaunch.h"
26 #endif
28 #include <algorithm>
30 namespace mozilla {
31 namespace ipc {
33 LazyLogModule gForkServiceLog("ForkService");
35 ForkServer::ForkServer() {}
37 /**
38 * Prepare an environment for running a fork server.
40 void ForkServer::InitProcess(int* aArgc, char*** aArgv) {
41 base::InitForkServerProcess();
43 mTcver = MakeUnique<MiniTransceiver>(kClientPipeFd,
44 DataBufferClear::AfterReceiving);
47 /**
48 * Preload any resources that the forked child processes might need,
49 * and which might change incompatibly or become unavailable by the
50 * time they're started. For example: the omnijar files, or certain
51 * shared libraries.
53 static void ForkServerPreload(int& aArgc, char** aArgv) {
54 Omnijar::ChildProcessInit(aArgc, aArgv);
57 /**
58 * Start providing the service at the IPC channel.
60 bool ForkServer::HandleMessages() {
61 while (true) {
62 UniquePtr<IPC::Message> msg;
63 if (!mTcver->Recv(msg)) {
64 break;
67 OnMessageReceived(std::move(msg));
69 if (mAppProcBuilder) {
70 // New process - child
71 return false;
74 // Stop the server
75 return true;
78 inline void CleanCString(nsCString& str) {
79 char* data;
80 int sz = str.GetMutableData(&data);
82 memset(data, ' ', sz);
85 inline void CleanString(std::string& str) {
86 const char deadbeef[] =
87 "\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef"
88 "\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
89 int pos = 0;
90 size_t sz = str.size();
91 while (sz > 0) {
92 int toclean = std::min(sz, sizeof(deadbeef) - 1);
93 str.replace(pos, toclean, deadbeef);
94 sz -= toclean;
95 pos += toclean;
99 inline void PrepareArguments(std::vector<std::string>& aArgv,
100 nsTArray<nsCString>& aArgvArray) {
101 for (auto& elt : aArgvArray) {
102 aArgv.push_back(elt.get());
103 CleanCString(elt);
107 // Prepare aOptions->env_map
108 inline void PrepareEnv(base::LaunchOptions* aOptions,
109 nsTArray<EnvVar>& aEnvMap) {
110 for (auto& elt : aEnvMap) {
111 nsCString& var = std::get<0>(elt);
112 nsCString& val = std::get<1>(elt);
113 aOptions->env_map[var.get()] = val.get();
114 CleanCString(var);
115 CleanCString(val);
119 // Prepare aOptions->fds_to_remap
120 inline void PrepareFdsRemap(base::LaunchOptions* aOptions,
121 nsTArray<FdMapping>& aFdsRemap) {
122 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, ("fds mapping:"));
123 for (auto& elt : aFdsRemap) {
124 // FDs are duplicated here.
125 int fd = std::get<0>(elt).ClonePlatformHandle().release();
126 std::pair<int, int> fdmap(fd, std::get<1>(elt));
127 aOptions->fds_to_remap.push_back(fdmap);
128 MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
129 ("\t%d => %d", fdmap.first, fdmap.second));
133 template <class P>
134 static void ReadParamInfallible(IPC::MessageReader* aReader, P* aResult,
135 const char* aCrashMessage) {
136 if (!IPC::ReadParam(aReader, aResult)) {
137 MOZ_CRASH_UNSAFE(aCrashMessage);
142 * Parse a Message to get a list of arguments and fill a LaunchOptions.
144 inline bool ParseForkNewSubprocess(IPC::Message& aMsg,
145 std::vector<std::string>& aArgv,
146 base::LaunchOptions* aOptions) {
147 if (aMsg.type() != Msg_ForkNewSubprocess__ID) {
148 MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
149 ("unknown message type %d\n", aMsg.type()));
150 return false;
153 IPC::MessageReader reader(aMsg);
154 nsTArray<nsCString> argv_array;
155 nsTArray<EnvVar> env_map;
156 nsTArray<FdMapping> fds_remap;
158 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
159 ReadParamInfallible(&reader, &aOptions->fork_flags,
160 "Error deserializing 'int'");
161 ReadParamInfallible(&reader, &aOptions->sandbox_chroot,
162 "Error deserializing 'bool'");
163 #endif
164 ReadParamInfallible(&reader, &argv_array,
165 "Error deserializing 'nsCString[]'");
166 ReadParamInfallible(&reader, &env_map, "Error deserializing 'EnvVar[]'");
167 ReadParamInfallible(&reader, &fds_remap, "Error deserializing 'FdMapping[]'");
168 reader.EndRead();
170 PrepareArguments(aArgv, argv_array);
171 PrepareEnv(aOptions, env_map);
172 PrepareFdsRemap(aOptions, fds_remap);
174 return true;
177 inline void SanitizeBuffers(IPC::Message& aMsg, std::vector<std::string>& aArgv,
178 base::LaunchOptions& aOptions) {
179 // Clean all buffers in the message to make sure content processes
180 // not peeking others.
181 auto& blist = aMsg.Buffers();
182 for (auto itr = blist.Iter(); !itr.Done();
183 itr.Advance(blist, itr.RemainingInSegment())) {
184 memset(itr.Data(), 0, itr.RemainingInSegment());
187 // clean all data string made from the message.
188 for (auto& var : aOptions.env_map) {
189 // Do it anyway since it is not going to be used anymore.
190 CleanString(*const_cast<std::string*>(&var.first));
191 CleanString(var.second);
193 for (auto& arg : aArgv) {
194 CleanString(arg);
199 * Extract parameters from the |Message| to create a
200 * |base::AppProcessBuilder| as |mAppProcBuilder|.
202 * It will return in both the fork server process and the new content
203 * process. |mAppProcBuilder| is null for the fork server.
205 void ForkServer::OnMessageReceived(UniquePtr<IPC::Message> message) {
206 std::vector<std::string> argv;
207 base::LaunchOptions options;
208 if (!ParseForkNewSubprocess(*message, argv, &options)) {
209 return;
212 base::ProcessHandle child_pid = -1;
213 mAppProcBuilder = MakeUnique<base::AppProcessBuilder>();
214 if (!mAppProcBuilder->ForkProcess(argv, std::move(options), &child_pid)) {
215 MOZ_CRASH("fail to fork");
217 MOZ_ASSERT(child_pid >= 0);
219 if (child_pid == 0) {
220 // Content process
221 return;
224 // Fork server process
226 mAppProcBuilder = nullptr;
228 IPC::Message reply(MSG_ROUTING_CONTROL, Reply_ForkNewSubprocess__ID);
229 IPC::MessageWriter writer(reply);
230 WriteIPDLParam(&writer, nullptr, child_pid);
231 mTcver->SendInfallible(reply, "failed to send a reply message");
233 // Without this, the content processes that is forked later are
234 // able to read the content of buffers even the buffers have been
235 // released.
236 SanitizeBuffers(*message, argv, options);
240 * Setup and run a fork server at the main thread.
242 * This function returns for two reasons:
243 * - the fork server is stopped normally, or
244 * - a new process is forked from the fork server and this function
245 * returned in the child, the new process.
247 * For the later case, aArgc and aArgv are modified to pass the
248 * arguments from the chrome process.
250 bool ForkServer::RunForkServer(int* aArgc, char*** aArgv) {
251 #ifdef DEBUG
252 if (getenv("MOZ_FORKSERVER_WAIT_GDB")) {
253 printf(
254 "Waiting for 30 seconds."
255 " Attach the fork server with gdb %s %d\n",
256 (*aArgv)[0], base::GetCurrentProcId());
257 sleep(30);
259 bool sleep_newproc = !!getenv("MOZ_FORKSERVER_WAIT_GDB_NEWPROC");
260 #endif
262 SetProcessTitleInit(*aArgv);
264 // Do this before NS_LogInit() to avoid log files taking lower
265 // FDs.
266 ForkServer forkserver;
267 forkserver.InitProcess(aArgc, aArgv);
269 XRE_SetProcessType("forkserver");
270 NS_LogInit();
271 mozilla::LogModule::Init(0, nullptr);
272 ForkServerPreload(*aArgc, *aArgv);
273 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, ("Start a fork server"));
275 DebugOnly<base::ProcessHandle> forkserver_pid = base::GetCurrentProcId();
276 if (forkserver.HandleMessages()) {
277 // In the fork server process
278 // The server has stopped.
279 MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
280 ("Terminate the fork server"));
281 Omnijar::CleanUp();
282 NS_LogTerm();
283 return true;
285 // Now, we are running in a content process just forked from
286 // the fork server process.
287 MOZ_ASSERT(base::GetCurrentProcId() != forkserver_pid);
288 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, ("Fork a new content process"));
290 #ifdef DEBUG
291 if (sleep_newproc) {
292 printf(
293 "Waiting for 30 seconds."
294 " Attach the new process with gdb %s %d\n",
295 (*aArgv)[0], base::GetCurrentProcId());
296 sleep(30);
298 #endif
299 NS_LogTerm();
301 MOZ_ASSERT(forkserver.mAppProcBuilder);
302 // |messageloop| has been destroyed. So, we can intialized the
303 // process safely. Message loops may allocates some file
304 // descriptors. If it is destroyed later, it may mess up this
305 // content process by closing wrong file descriptors.
306 forkserver.mAppProcBuilder->InitAppProcess(aArgc, aArgv);
307 forkserver.mAppProcBuilder.reset();
309 // Open log files again with right names and the new PID.
310 nsTraceRefcnt::ResetLogFiles((*aArgv)[*aArgc - 1]);
312 return false;
315 } // namespace ipc
316 } // namespace mozilla