ForkServiceChild.cpp (9628B)
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 "ForkServiceChild.h" 8 #include "ForkServer.h" 9 #include "chrome/common/process_watcher.h" 10 #include "mozilla/Atomics.h" 11 #include "mozilla/Logging.h" 12 #include "mozilla/ipc/GeckoChildProcessHost.h" 13 #include "mozilla/ipc/ProtocolMessageUtils.h" 14 #include "mozilla/StaticPrefs_dom.h" 15 #include "mozilla/Services.h" 16 #include "ipc/IPCMessageUtilsSpecializations.h" 17 #include "nsIObserverService.h" 18 19 #include <unistd.h> 20 #include <fcntl.h> 21 22 namespace mozilla { 23 namespace ipc { 24 25 extern LazyLogModule gForkServiceLog; 26 27 StaticMutex ForkServiceChild::sMutex; 28 StaticRefPtr<ForkServiceChild> ForkServiceChild::sSingleton; 29 Atomic<bool> ForkServiceChild::sForkServiceUsed; 30 31 #ifndef SOCK_CLOEXEC 32 static bool ConfigurePipeFd(int aFd) { 33 int flags = fcntl(aFd, F_GETFD, 0); 34 return flags != -1 && fcntl(aFd, F_SETFD, flags | FD_CLOEXEC) != -1; 35 } 36 #endif 37 38 // Create a socketpair with both ends marked as close-on-exec 39 static Result<Ok, LaunchError> CreateSocketPair(UniqueFileHandle& aFD0, 40 UniqueFileHandle& aFD1) { 41 int fds[2]; 42 #ifdef SOCK_CLOEXEC 43 constexpr int type = SOCK_STREAM | SOCK_CLOEXEC; 44 #else 45 constexpr int type = SOCK_STREAM; 46 #endif 47 48 if (socketpair(AF_UNIX, type, 0, fds) < 0) { 49 return Err(LaunchError("FSC::CSP::sp", errno)); 50 } 51 52 #ifndef SOCK_CLOEXEC 53 if (!ConfigurePipeFd(server.get()) || !ConfigurePipeFd(client.get())) { 54 return Err(LaunchError("FSC::CSP::cfg", errno)); 55 } 56 #endif 57 58 aFD0.reset(fds[0]); 59 aFD1.reset(fds[1]); 60 61 return Ok(); 62 } 63 64 void ForkServiceChild::StartForkServer() { 65 UniqueFileHandle server; 66 UniqueFileHandle client; 67 if (CreateSocketPair(server, client).isErr()) { 68 MOZ_LOG(gForkServiceLog, LogLevel::Error, 69 ("failed to create fork server socket")); 70 return; 71 } 72 73 GeckoChildProcessHost* subprocess = 74 new GeckoChildProcessHost(GeckoProcessType_ForkServer, false); 75 76 geckoargs::ChildProcessArgs extraOpts; 77 geckoargs::sIPCHandle.Put(std::move(client), extraOpts); 78 geckoargs::sSignalPipe.Put(ProcessWatcher::GetSignalPipe(), extraOpts); 79 80 if (!subprocess->LaunchAndWaitForProcessHandle(std::move(extraOpts))) { 81 MOZ_LOG(gForkServiceLog, LogLevel::Error, ("failed to launch fork server")); 82 return; 83 } 84 85 sForkServiceUsed = true; 86 StaticMutexAutoLock smal(sMutex); 87 // Can't use MakeRefPtr; ctor is private. 88 MOZ_ASSERT(sSingleton == nullptr); 89 sSingleton = new ForkServiceChild(server.release(), subprocess); 90 } 91 92 void ForkServiceChild::StopForkServer() { 93 RefPtr<ForkServiceChild> oldChild; 94 { 95 StaticMutexAutoLock smal(sMutex); 96 oldChild = sSingleton.forget(); 97 } 98 // Drop the old reference outside of the lock to avoid lock order 99 // cycles via the GeckoChildProcessHost dtor. 100 } 101 102 RefPtr<ForkServiceChild> ForkServiceChild::Get() { 103 RefPtr<ForkServiceChild> child; 104 { 105 StaticMutexAutoLock smal(sMutex); 106 child = sSingleton; 107 } 108 return child; 109 } 110 111 ForkServiceChild::ForkServiceChild(int aFd, GeckoChildProcessHost* aProcess) 112 : mMutex("mozilla.ipc.ForkServiceChild.mMutex"), 113 mFailed(false), 114 mProcess(aProcess) { 115 mTcver = MakeUnique<MiniTransceiver>(aFd); 116 } 117 118 ForkServiceChild::~ForkServiceChild() { 119 close(mTcver->GetFD()); 120 // This can be synchronous during browser shutdown, so do it *after* 121 // causing the fork server to exit by closning the socket: 122 mProcess->Destroy(); 123 } 124 125 Result<Ok, LaunchError> ForkServiceChild::SendForkNewSubprocess( 126 geckoargs::ChildProcessArgs&& aArgs, base::LaunchOptions&& aOptions, 127 pid_t* aPid) { 128 // Double-check there are no unsupported options. 129 MOZ_ASSERT(aOptions.workdir.empty()); 130 MOZ_ASSERT(!aOptions.full_env); 131 MOZ_ASSERT(!aOptions.wait); 132 MOZ_ASSERT(aOptions.fds_to_remap.size() == aArgs.mFiles.size()); 133 134 MutexAutoLock lock(mMutex); 135 if (mFailed) { 136 return Err(LaunchError("FSC::SFNS::Failed")); 137 } 138 139 UniqueFileHandle execParent; 140 { 141 UniqueFileHandle execChild; 142 IPC::Message msg(MSG_ROUTING_CONTROL, Msg_ForkNewSubprocess__ID); 143 144 MOZ_TRY(CreateSocketPair(execParent, execChild)); 145 146 IPC::MessageWriter writer(msg); 147 #if defined(XP_LINUX) && defined(MOZ_SANDBOX) 148 WriteParam(&writer, aOptions.fork_flags); 149 WriteParam(&writer, std::move(aOptions.sandbox_chroot_server)); 150 #endif 151 WriteParam(&writer, std::move(execChild)); 152 if (!mTcver->Send(msg)) { 153 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, 154 ("the pipe to the fork server is closed or having errors")); 155 OnError(); 156 return Err(LaunchError("FSC::SFNS::Send")); 157 } 158 } 159 160 { 161 MiniTransceiver execTcver(execParent.get()); 162 IPC::Message execMsg(MSG_ROUTING_CONTROL, Msg_SubprocessExecInfo__ID); 163 IPC::MessageWriter execWriter(execMsg); 164 WriteParam(&execWriter, aOptions.env_map); 165 WriteParam(&execWriter, aArgs.mArgs); 166 WriteParam(&execWriter, std::move(aArgs.mFiles)); 167 if (!execTcver.Send(execMsg)) { 168 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, 169 ("failed to send exec info to the fork server")); 170 OnError(); 171 return Err(LaunchError("FSC::SFNS::Send2")); 172 } 173 } 174 execParent = nullptr; 175 176 UniquePtr<IPC::Message> reply; 177 if (!mTcver->Recv(reply)) { 178 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, 179 ("the pipe to the fork server is closed or having errors")); 180 OnError(); 181 return Err(LaunchError("FSC::SFNS::Recv")); 182 } 183 184 if (reply->type() != Reply_ForkNewSubprocess__ID) { 185 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, 186 ("unknown reply type %d", reply->type())); 187 return Err(LaunchError("FSC::SFNS::Type")); 188 } 189 IPC::MessageReader reader(*reply); 190 191 if (!ReadParam(&reader, aPid)) { 192 MOZ_CRASH("Error deserializing 'pid_t'"); 193 } 194 reader.EndRead(); 195 196 return Ok(); 197 } 198 199 auto ForkServiceChild::SendWaitPid(pid_t aPid, bool aBlock) 200 -> Result<ProcStatus, int> { 201 MutexAutoLock lock(mMutex); 202 if (mFailed) { 203 return Err(ECONNRESET); 204 } 205 206 IPC::Message msg(MSG_ROUTING_CONTROL, Msg_WaitPid__ID); 207 IPC::MessageWriter writer(msg); 208 WriteParam(&writer, aPid); 209 WriteParam(&writer, aBlock); 210 211 if (!mTcver->Send(msg)) { 212 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, 213 ("the pipe to the fork server is closed or having errors")); 214 OnError(); 215 return Err(ECONNRESET); 216 } 217 218 UniquePtr<IPC::Message> reply; 219 if (!mTcver->Recv(reply)) { 220 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, 221 ("the pipe to the fork server is closed or having errors")); 222 OnError(); 223 return Err(ECONNRESET); 224 } 225 226 if (reply->type() != Reply_WaitPid__ID) { 227 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, 228 ("unknown reply type %d", reply->type())); 229 OnError(); 230 return Err(EPROTO); 231 } 232 IPC::MessageReader reader(*reply); 233 234 // Both sides of the Result are isomorphic to int. 235 bool isErr = false; 236 int value = 0; 237 if (!ReadParam(&reader, &isErr) || !ReadParam(&reader, &value)) { 238 MOZ_LOG(gForkServiceLog, LogLevel::Verbose, 239 ("deserialization error in waitpid reply")); 240 OnError(); 241 return Err(EPROTO); 242 } 243 244 // This can't use ?: because the types are different. 245 if (isErr) { 246 return Err(value); 247 } 248 return ProcStatus{value}; 249 } 250 251 void ForkServiceChild::OnError() { 252 mFailed = true; 253 ForkServerLauncher::RestartForkServer(); 254 } 255 256 NS_IMPL_ISUPPORTS(ForkServerLauncher, nsIObserver) 257 258 bool ForkServerLauncher::sHaveStartedClient = false; 259 StaticRefPtr<ForkServerLauncher> ForkServerLauncher::sSingleton; 260 261 ForkServerLauncher::ForkServerLauncher() {} 262 263 ForkServerLauncher::~ForkServerLauncher() {} 264 265 already_AddRefed<ForkServerLauncher> ForkServerLauncher::Create() { 266 if (sSingleton == nullptr) { 267 sSingleton = new ForkServerLauncher(); 268 } 269 RefPtr<ForkServerLauncher> launcher = sSingleton; 270 return launcher.forget(); 271 } 272 273 NS_IMETHODIMP 274 ForkServerLauncher::Observe(nsISupports* aSubject, const char* aTopic, 275 const char16_t* aData) { 276 if (strcmp(aTopic, NS_XPCOM_STARTUP_CATEGORY) == 0) { 277 nsCOMPtr<nsIObserverService> obsSvc = 278 mozilla::services::GetObserverService(); 279 MOZ_ASSERT(obsSvc != nullptr); 280 // preferences are not available until final-ui-startup 281 obsSvc->AddObserver(this, "final-ui-startup", false); 282 } else if (!sHaveStartedClient && strcmp(aTopic, "final-ui-startup") == 0) { 283 if (StaticPrefs::dom_ipc_forkserver_enable_AtStartup()) { 284 sHaveStartedClient = true; 285 ForkServiceChild::StartForkServer(); 286 287 nsCOMPtr<nsIObserverService> obsSvc = 288 mozilla::services::GetObserverService(); 289 MOZ_ASSERT(obsSvc != nullptr); 290 obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); 291 } else { 292 sSingleton = nullptr; 293 } 294 } 295 296 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { 297 // To make leak checker happy! 298 sSingleton = nullptr; 299 } 300 301 return NS_OK; 302 } 303 304 void ForkServerLauncher::RestartForkServer() { 305 // Restart fork server 306 NS_SUCCEEDED(NS_DispatchToMainThreadQueue( 307 NS_NewRunnableFunction("OnForkServerError", 308 [] { 309 if (sSingleton) { 310 ForkServiceChild::StopForkServer(); 311 ForkServiceChild::StartForkServer(); 312 } 313 }), 314 EventQueuePriority::Idle)); 315 } 316 317 } // namespace ipc 318 } // namespace mozilla