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"
24 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
25 # include "mozilla/SandboxLaunch.h"
33 LazyLogModule
gForkServiceLog("ForkService");
35 ForkServer::ForkServer() {}
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
);
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
53 static void ForkServerPreload(int& aArgc
, char** aArgv
) {
54 Omnijar::ChildProcessInit(aArgc
, aArgv
);
58 * Start providing the service at the IPC channel.
60 bool ForkServer::HandleMessages() {
62 UniquePtr
<IPC::Message
> msg
;
63 if (!mTcver
->Recv(msg
)) {
67 OnMessageReceived(std::move(msg
));
69 if (mAppProcBuilder
) {
70 // New process - child
78 inline void CleanCString(nsCString
& str
) {
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";
90 size_t sz
= str
.size();
92 int toclean
= std::min(sz
, sizeof(deadbeef
) - 1);
93 str
.replace(pos
, toclean
, deadbeef
);
99 inline void PrepareArguments(std::vector
<std::string
>& aArgv
,
100 nsTArray
<nsCString
>& aArgvArray
) {
101 for (auto& elt
: aArgvArray
) {
102 aArgv
.push_back(elt
.get());
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();
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
));
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()));
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'");
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[]'");
170 PrepareArguments(aArgv
, argv_array
);
171 PrepareEnv(aOptions
, env_map
);
172 PrepareFdsRemap(aOptions
, fds_remap
);
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
) {
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
)) {
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) {
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
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
) {
252 if (getenv("MOZ_FORKSERVER_WAIT_GDB")) {
254 "Waiting for 30 seconds."
255 " Attach the fork server with gdb %s %d\n",
256 (*aArgv
)[0], base::GetCurrentProcId());
259 bool sleep_newproc
= !!getenv("MOZ_FORKSERVER_WAIT_GDB_NEWPROC");
262 SetProcessTitleInit(*aArgv
);
264 // Do this before NS_LogInit() to avoid log files taking lower
266 ForkServer forkserver
;
267 forkserver
.InitProcess(aArgc
, aArgv
);
269 XRE_SetProcessType("forkserver");
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"));
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"));
293 "Waiting for 30 seconds."
294 " Attach the new process with gdb %s %d\n",
295 (*aArgv
)[0], base::GetCurrentProcId());
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]);
316 } // namespace mozilla