ForkServer.cpp (13306B)
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/. */ 6 7 #include "mozilla/ipc/ForkServer.h" 8 9 #include "base/eintr_wrapper.h" 10 #include "chrome/common/chrome_switches.h" 11 #include "ipc/IPCMessageUtilsSpecializations.h" 12 #include "mozilla/BlockingResourceBase.h" 13 #include "mozilla/GeckoArgs.h" 14 #include "mozilla/Logging.h" 15 #include "mozilla/Omnijar.h" 16 #include "mozilla/ProcessType.h" 17 #include "mozilla/ipc/FileDescriptor.h" 18 #include "mozilla/ipc/ProcessUtils.h" 19 #include "mozilla/ipc/ProtocolMessageUtils.h" 20 #include "mozilla/ipc/SetProcessTitle.h" 21 #include "nsTraceRefcnt.h" 22 23 #include <fcntl.h> 24 #include <string.h> 25 #include <sys/wait.h> 26 #include <unistd.h> 27 28 #if defined(XP_LINUX) && defined(MOZ_SANDBOX) 29 # include "mozilla/SandboxLaunch.h" 30 #endif 31 32 #if defined(XP_OPENBSD) 33 # include "BinaryPath.h" 34 # include <err.h> 35 #endif 36 37 #include <algorithm> 38 39 namespace mozilla { 40 namespace ipc { 41 42 LazyLogModule gForkServiceLog("ForkService"); 43 44 static int gSignalPipe = -1; 45 static void HandleSigChld(int aSignal) { 46 MOZ_ASSERT(aSignal == SIGCHLD); 47 const char msg = 0; 48 HANDLE_EINTR(write(gSignalPipe, &msg, 1)); 49 } 50 51 ForkServer::ForkServer(int* aArgc, char*** aArgv) : mArgc(aArgc), mArgv(aArgv) { 52 SetThisProcessName("forkserver"); 53 54 Maybe<UniqueFileHandle> ipcHandle = geckoargs::sIPCHandle.Get(*aArgc, *aArgv); 55 if (!ipcHandle) { 56 MOZ_CRASH("forkserver missing ipcHandle argument"); 57 } 58 59 // Hold our IPC FD while our MiniTransceiver is alive. 60 mIpcFd = ipcHandle.extract(); 61 mTcver = MakeUnique<MiniTransceiver>(mIpcFd.get(), 62 DataBufferClear::AfterReceiving); 63 64 auto signalPipe = geckoargs::sSignalPipe.Get(*aArgc, *aArgv); 65 if (signalPipe) { 66 gSignalPipe = signalPipe->release(); 67 signal(SIGCHLD, HandleSigChld); 68 } else { 69 signal(SIGCHLD, SIG_IGN); 70 } 71 } 72 73 /** 74 * Preload any resources that the forked child processes might need, 75 * and which might change incompatibly or become unavailable by the 76 * time they're started. For example: the omnijar files, or certain 77 * shared libraries. 78 */ 79 static void ForkServerPreload(int& aArgc, char** aArgv) { 80 Omnijar::ChildProcessInit(aArgc, aArgv); 81 #if defined(XP_OPENBSD) 82 char binaryPath[MAXPATHLEN]; 83 nsresult rv = mozilla::BinaryPath::Get(binaryPath); 84 if (NS_FAILED(rv)) { 85 errx(1, "failed to cache binary path ?"); 86 } 87 #endif 88 } 89 90 /** 91 * Start providing the service at the IPC channel. 92 */ 93 bool ForkServer::HandleMessages() { 94 while (true) { 95 UniquePtr<IPC::Message> msg; 96 if (!mTcver->Recv(msg)) { 97 break; 98 } 99 100 switch (msg->type()) { 101 case Msg_ForkNewSubprocess__ID: 102 if (HandleForkNewSubprocess(std::move(msg))) { 103 // New process - child 104 return false; 105 } 106 break; 107 case Msg_WaitPid__ID: 108 HandleWaitPid(std::move(msg)); 109 break; 110 default: 111 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, 112 ("unknown message type %d\n", msg->type())); 113 } 114 } 115 // Stop the server 116 return true; 117 } 118 119 template <class P> 120 static void ReadParamInfallible(IPC::MessageReader* aReader, P* aResult, 121 const char* aCrashMessage) { 122 if (!IPC::ReadParam(aReader, aResult)) { 123 MOZ_CRASH_UNSAFE(aCrashMessage); 124 } 125 } 126 127 /** 128 * Parse a Message to obtain a `LaunchOptions` and the attached fd 129 * that the child will use to receive its `SubprocessExecInfo`. 130 */ 131 static bool ParseForkNewSubprocess(IPC::Message& aMsg, 132 UniqueFileHandle* aExecFd, 133 base::LaunchOptions* aOptions) { 134 // The type was already checked in HandleMessages 135 MOZ_ASSERT(aMsg.type() == Msg_ForkNewSubprocess__ID); 136 IPC::MessageReader reader(aMsg); 137 138 #if defined(XP_LINUX) && defined(MOZ_SANDBOX) 139 ReadParamInfallible(&reader, &aOptions->fork_flags, 140 "Error deserializing 'int'"); 141 ReadParamInfallible(&reader, &aOptions->sandbox_chroot_server, 142 "Error deserializing 'UniqueFileHandle'"); 143 #endif 144 ReadParamInfallible(&reader, aExecFd, 145 "Error deserializing 'UniqueFileHandle'"); 146 reader.EndRead(); 147 148 return true; 149 } 150 151 /** 152 * Parse a `Message`, in the forked child process, to get the argument 153 * and environment strings. 154 */ 155 static bool ParseSubprocessExecInfo(IPC::Message& aMsg, 156 geckoargs::ChildProcessArgs* aArgs, 157 base::environment_map* aEnv) { 158 if (aMsg.type() != Msg_SubprocessExecInfo__ID) { 159 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, 160 ("unknown message type %d (!= %d)\n", aMsg.type(), 161 Msg_SubprocessExecInfo__ID)); 162 return false; 163 } 164 165 IPC::MessageReader reader(aMsg); 166 167 ReadParamInfallible(&reader, aEnv, "Error deserializing 'env_map'"); 168 ReadParamInfallible(&reader, &aArgs->mArgs, "Error deserializing 'mArgs'"); 169 ReadParamInfallible(&reader, &aArgs->mFiles, "Error deserializing 'mFiles'"); 170 reader.EndRead(); 171 172 return true; 173 } 174 175 // Run in the forked child process. Receives a message on `aExecFd` containing 176 // the new process configuration, and updates the environment, command line, and 177 // passed file handles to reflect the new process. 178 static void ForkedChildProcessInit(int aExecFd, int* aArgc, char*** aArgv) { 179 // Remove the fork-server-specific SIGCHLD handler. 180 signal(SIGCHLD, SIG_DFL); 181 // This process is currently single-threaded, so the fd used by the 182 // signal handler can be safely closed once the handler is removed. 183 if (gSignalPipe >= 0) { 184 close(gSignalPipe); 185 gSignalPipe = -1; 186 } 187 188 // Content process 189 MiniTransceiver execTcver(aExecFd); 190 UniquePtr<IPC::Message> execMsg; 191 if (!execTcver.Recv(execMsg)) { 192 // Crashing here isn't great, because the crash reporter isn't 193 // set up, but we don't have a lot of options currently. Also, 194 // receive probably won't fail unless the parent also crashes. 195 printf_stderr("ForkServer: SubprocessExecInfo receive error\n"); 196 MOZ_CRASH(); 197 } 198 199 geckoargs::ChildProcessArgs args; 200 base::environment_map env; 201 if (!ParseSubprocessExecInfo(*execMsg, &args, &env)) { 202 printf_stderr("ForkServer: SubprocessExecInfo parse error\n"); 203 MOZ_CRASH(); 204 } 205 206 // Set environment variables as specified in env_map. 207 for (auto& elt : env) { 208 setenv(elt.first.c_str(), elt.second.c_str(), 1); 209 } 210 211 // Initialize passed file handles. 212 geckoargs::SetPassedFileHandles(std::move(args.mFiles)); 213 214 // Change argc & argv of main() with the arguments passing 215 // through IPC. 216 char** argv = new char*[args.mArgs.size() + 1]; 217 char** p = argv; 218 for (auto& elt : args.mArgs) { 219 *p++ = strdup(elt.c_str()); 220 } 221 *p = nullptr; 222 *aArgv = argv; 223 *aArgc = args.mArgs.size(); 224 mozilla::SetProcessTitle(args.mArgs); 225 } 226 227 /** 228 * Extract parameters from the |Message| to create a 229 * |base::AppProcessBuilder| as |mAppProcBuilder|. 230 * 231 * It will return in both the fork server process and the new content 232 * process. |mAppProcBuilder| is null for the fork server. 233 */ 234 bool ForkServer::HandleForkNewSubprocess(UniquePtr<IPC::Message> aMessage) { 235 UniqueFileHandle execFd; 236 base::LaunchOptions options; 237 if (!ParseForkNewSubprocess(*aMessage, &execFd, &options)) { 238 return false; 239 } 240 241 #if defined(MOZ_MEMORY) && defined(DEBUG) 242 jemalloc_stats_t stats; 243 jemalloc_stats(&stats); 244 // What we actually want to assert is that there are 0 thread-local arenas 245 // (threads may exist but thread-local arenas are opt-in) that would be leaked 246 // (because the threads wont exist in the new process), and 0 private 247 // main-thread-only arenas and this is not the main thread (as those might be 248 // inconsistent in the new process). Instead we check that there's exactly 249 // one arena - the default public arena). 250 MOZ_ASSERT(stats.narenas == 1, 251 "ForkServer before fork()/clone() should have a single arena."); 252 #endif 253 254 #if defined(XP_LINUX) && defined(MOZ_SANDBOX) 255 mozilla::SandboxLaunch launcher; 256 if (!launcher.Prepare(&options)) { 257 MOZ_CRASH("SandboxLaunch::Prepare failed"); 258 } 259 #else 260 struct { 261 pid_t Fork() { return fork(); } 262 } launcher; 263 #endif 264 265 // Avoid any contents of buffered stdout/stderr being sent by forked 266 // children. 267 fflush(stdout); 268 fflush(stderr); 269 270 pid_t pid = launcher.Fork(); 271 if (pid < 0) { 272 MOZ_CRASH("failed to fork"); 273 } 274 275 // NOTE: After this point, if pid == 0, we're in the newly forked child 276 // process. 277 278 if (pid == 0) { 279 // Re-configure to a child process, and return to our caller. 280 ForkedChildProcessInit(execFd.get(), mArgc, mArgv); 281 return true; 282 } 283 284 // Fork server process 285 286 IPC::Message reply(MSG_ROUTING_CONTROL, Reply_ForkNewSubprocess__ID); 287 IPC::MessageWriter writer(reply); 288 WriteParam(&writer, pid); 289 mTcver->SendInfallible(reply, "failed to send a reply message"); 290 291 return false; 292 } 293 294 void ForkServer::HandleWaitPid(UniquePtr<IPC::Message> aMessage) { 295 MOZ_ASSERT(aMessage->type() == Msg_WaitPid__ID); 296 IPC::MessageReader reader(*aMessage); 297 298 pid_t pid; 299 bool block; 300 ReadParamInfallible(&reader, &pid, "Error deserializing 'pid_t'"); 301 ReadParamInfallible(&reader, &block, "Error deserializing 'bool'"); 302 303 // It's safe to use plain waitpid here (and not the waitid/WNOWAIT 304 // contraption used in the parent process) because this process is 305 // single-threaded so there's no possibility of another thread 306 // trying to ptrace the same child process. 307 int status; 308 pid_t rv = HANDLE_EINTR(waitpid(pid, &status, block ? 0 : WNOHANG)); 309 // Three possibilities here: 310 // Terminated: rv > 0; return {false, status} 311 // Running: rv = 0; return {true, 0} 312 // Error: rv < 0; return {true, errno} 313 bool isErr = rv <= 0; 314 int err = rv < 0 ? errno : 0; 315 MOZ_ASSERT(isErr || rv == pid); 316 317 IPC::Message reply(MSG_ROUTING_CONTROL, Reply_WaitPid__ID); 318 IPC::MessageWriter writer(reply); 319 WriteParam(&writer, isErr); 320 WriteParam(&writer, isErr ? err : status); 321 mTcver->SendInfallible(reply, "failed to send a reply message"); 322 } 323 324 /** 325 * Setup and run a fork server at the main thread. 326 * 327 * This function returns for two reasons: 328 * - the fork server is stopped normally, or 329 * - a new process is forked from the fork server and this function 330 * returned in the child, the new process. 331 * 332 * For the later case, aArgc and aArgv are modified to pass the 333 * arguments from the chrome process. 334 */ 335 bool ForkServer::RunForkServer(int* aArgc, char*** aArgv) { 336 MOZ_ASSERT(XRE_IsForkServerProcess(), "fork server process only"); 337 338 #ifdef DEBUG 339 if (getenv("MOZ_FORKSERVER_WAIT_GDB")) { 340 printf( 341 "Waiting for 30 seconds." 342 " Attach the fork server with gdb %s %d\n", 343 (*aArgv)[0], base::GetCurrentProcId()); 344 sleep(30); 345 } 346 bool sleep_newproc = !!getenv("MOZ_FORKSERVER_WAIT_GDB_NEWPROC"); 347 #endif 348 349 SetProcessTitleInit(*aArgv); 350 351 // Do this before NS_LogInit() to avoid log files taking lower 352 // FDs. 353 ForkServer forkserver(aArgc, aArgv); 354 355 NS_LogInit(); 356 mozilla::LogModule::Init(0, nullptr); 357 ForkServerPreload(*aArgc, *aArgv); 358 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, ("Start a fork server")); 359 { 360 DebugOnly<base::ProcessHandle> forkserver_pid = base::GetCurrentProcId(); 361 if (forkserver.HandleMessages()) { 362 // In the fork server process 363 // The server has stopped. 364 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, 365 ("Terminate the fork server")); 366 Omnijar::CleanUp(); 367 NS_LogTerm(); 368 return true; 369 } 370 // Now, we are running in a content process just forked from 371 // the fork server process. 372 MOZ_ASSERT(base::GetCurrentProcId() != forkserver_pid); 373 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, ("Fork a new content process")); 374 } 375 #ifdef DEBUG 376 if (sleep_newproc) { 377 printf( 378 "Waiting for 30 seconds." 379 " Attach the new process with gdb %s %d\n", 380 (*aArgv)[0], base::GetCurrentProcId()); 381 sleep(30); 382 } 383 #endif 384 NS_LogTerm(); 385 386 nsTraceRefcnt::CloseLogFilesAfterFork(); 387 388 // Update our GeckoProcessType and GeckoChildID, removing the arguments. 389 if (*aArgc < 2) { 390 MOZ_CRASH("forked process missing process type and childid arguments"); 391 } 392 SetGeckoProcessType((*aArgv)[--*aArgc]); 393 SetGeckoChildID((*aArgv)[--*aArgc]); 394 MOZ_ASSERT(!XRE_IsForkServerProcess(), 395 "fork server created another fork server?"); 396 397 // This is now a child process, and it may even be a Content process. 398 // It is required that the PRNG at least is re-initialized so the same state 399 // is not shared accross all child processes, and in case of a Content process 400 // it is also required that the small allocation are not being randomized ; 401 // failing to do so will lead to performance regressions, e.g. as in 402 // bug 1912262. 403 #if defined(MOZ_MEMORY) 404 jemalloc_reset_small_alloc_randomization( 405 /* aRandomizeSmall */ !XRE_IsContentProcess()); 406 #endif 407 408 // Open log files again with right names and the new PID. 409 nsTraceRefcnt::ReopenLogFilesAfterFork(XRE_GetProcessTypeString()); 410 411 return false; 412 } 413 414 } // namespace ipc 415 } // namespace mozilla