1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/child_process_launcher.h" 6 7 #include <utility> // For std::pair. 8 9 #include "base/bind.h" 10 #include "base/command_line.h" 11 #include "base/files/file_util.h" 12 #include "base/files/scoped_file.h" 13 #include "base/logging.h" 14 #include "base/memory/scoped_ptr.h" 15 #include "base/metrics/histogram.h" 16 #include "base/process/process.h" 17 #include "base/synchronization/lock.h" 18 #include "base/threading/thread.h" 19 #include "content/public/browser/browser_thread.h" 20 #include "content/public/browser/content_browser_client.h" 21 #include "content/public/common/content_descriptors.h" 22 #include "content/public/common/content_switches.h" 23 #include "content/public/common/result_codes.h" 24 #include "content/public/common/sandboxed_process_launcher_delegate.h" 25 26 #if defined(OS_WIN) 27 #include "base/files/file_path.h" 28 #include "content/common/sandbox_win.h" 29 #include "content/public/common/sandbox_init.h" 30 #elif defined(OS_MACOSX) 31 #include "content/browser/bootstrap_sandbox_mac.h" 32 #include "content/browser/mach_broker_mac.h" 33 #include "sandbox/mac/bootstrap_sandbox.h" 34 #elif defined(OS_ANDROID) 35 #include "base/android/jni_android.h" 36 #include "content/browser/android/child_process_launcher_android.h" 37 #elif defined(OS_POSIX) 38 #include "base/memory/shared_memory.h" 39 #include "base/memory/singleton.h" 40 #include "content/browser/renderer_host/render_sandbox_host_linux.h" 41 #include "content/browser/zygote_host/zygote_host_impl_linux.h" 42 #include "content/common/child_process_sandbox_support_impl_linux.h" 43 #endif 44 45 #if defined(OS_POSIX) 46 #include "base/metrics/stats_table.h" 47 #include "base/posix/global_descriptors.h" 48 #endif 49 50 namespace content { 51 52 // Having the functionality of ChildProcessLauncher be in an internal 53 // ref counted object allows us to automatically terminate the process when the 54 // parent class destructs, while still holding on to state that we need. 55 class ChildProcessLauncher::Context 56 : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> { 57 public: 58 Context() 59 : client_(NULL), 60 client_thread_id_(BrowserThread::UI), 61 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), 62 exit_code_(RESULT_CODE_NORMAL_EXIT), 63 starting_(true), 64 // TODO(earthdok): Re-enable on CrOS http://crbug.com/360622 65 #if (defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ 66 defined(THREAD_SANITIZER)) && !defined(OS_CHROMEOS) 67 terminate_child_on_shutdown_(false) 68 #else 69 terminate_child_on_shutdown_(true) 70 #endif 71 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 72 , zygote_(false) 73 #endif 74 { 75 } 76 77 void Launch( 78 SandboxedProcessLauncherDelegate* delegate, 79 base::CommandLine* cmd_line, 80 int child_process_id, 81 Client* client) { 82 client_ = client; 83 84 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); 85 86 #if defined(OS_ANDROID) 87 // We need to close the client end of the IPC channel to reliably detect 88 // child termination. We will close this fd after we create the child 89 // process which is asynchronous on Android. 90 ipcfd_ = delegate->GetIpcFd(); 91 #endif 92 BrowserThread::PostTask( 93 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 94 base::Bind( 95 &Context::LaunchInternal, 96 make_scoped_refptr(this), 97 client_thread_id_, 98 child_process_id, 99 delegate, 100 cmd_line)); 101 } 102 103 #if defined(OS_ANDROID) 104 static void OnChildProcessStarted( 105 // |this_object| is NOT thread safe. Only use it to post a task back. 106 scoped_refptr<Context> this_object, 107 BrowserThread::ID client_thread_id, 108 const base::TimeTicks begin_launch_time, 109 base::ProcessHandle handle) { 110 RecordHistograms(begin_launch_time); 111 if (BrowserThread::CurrentlyOn(client_thread_id)) { 112 // This is always invoked on the UI thread which is commonly the 113 // |client_thread_id| so we can shortcut one PostTask. 114 this_object->Notify(handle); 115 } else { 116 BrowserThread::PostTask( 117 client_thread_id, FROM_HERE, 118 base::Bind( 119 &ChildProcessLauncher::Context::Notify, 120 this_object, 121 handle)); 122 } 123 } 124 #endif 125 126 void ResetClient() { 127 // No need for locking as this function gets called on the same thread that 128 // client_ would be used. 129 CHECK(BrowserThread::CurrentlyOn(client_thread_id_)); 130 client_ = NULL; 131 } 132 133 void set_terminate_child_on_shutdown(bool terminate_on_shutdown) { 134 terminate_child_on_shutdown_ = terminate_on_shutdown; 135 } 136 137 private: 138 friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>; 139 friend class ChildProcessLauncher; 140 141 ~Context() { 142 Terminate(); 143 } 144 145 static void RecordHistograms(const base::TimeTicks begin_launch_time) { 146 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; 147 if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) { 148 RecordLaunchHistograms(launch_time); 149 } else { 150 BrowserThread::PostTask( 151 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 152 base::Bind(&ChildProcessLauncher::Context::RecordLaunchHistograms, 153 launch_time)); 154 } 155 } 156 157 static void RecordLaunchHistograms(const base::TimeDelta launch_time) { 158 // Log the launch time, separating out the first one (which will likely be 159 // slower due to the rest of the browser initializing at the same time). 160 static bool done_first_launch = false; 161 if (done_first_launch) { 162 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time); 163 } else { 164 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time); 165 done_first_launch = true; 166 } 167 } 168 169 static void LaunchInternal( 170 // |this_object| is NOT thread safe. Only use it to post a task back. 171 scoped_refptr<Context> this_object, 172 BrowserThread::ID client_thread_id, 173 int child_process_id, 174 SandboxedProcessLauncherDelegate* delegate, 175 base::CommandLine* cmd_line) { 176 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate); 177 #if defined(OS_WIN) 178 bool launch_elevated = delegate->ShouldLaunchElevated(); 179 #elif defined(OS_ANDROID) 180 int ipcfd = delegate->GetIpcFd(); 181 #elif defined(OS_MACOSX) 182 base::EnvironmentMap env = delegate->GetEnvironment(); 183 int ipcfd = delegate->GetIpcFd(); 184 #elif defined(OS_POSIX) 185 bool use_zygote = delegate->ShouldUseZygote(); 186 base::EnvironmentMap env = delegate->GetEnvironment(); 187 int ipcfd = delegate->GetIpcFd(); 188 #endif 189 scoped_ptr<base::CommandLine> cmd_line_deleter(cmd_line); 190 base::TimeTicks begin_launch_time = base::TimeTicks::Now(); 191 192 #if defined(OS_WIN) 193 base::ProcessHandle handle = base::kNullProcessHandle; 194 if (launch_elevated) { 195 base::LaunchOptions options; 196 options.start_hidden = true; 197 base::LaunchElevatedProcess(*cmd_line, options, &handle); 198 } else { 199 handle = StartSandboxedProcess(delegate, cmd_line); 200 } 201 #elif defined(OS_POSIX) 202 std::string process_type = 203 cmd_line->GetSwitchValueASCII(switches::kProcessType); 204 std::vector<FileDescriptorInfo> files_to_register; 205 files_to_register.push_back( 206 FileDescriptorInfo(kPrimaryIPCChannel, 207 base::FileDescriptor(ipcfd, false))); 208 base::StatsTable* stats_table = base::StatsTable::current(); 209 if (stats_table && 210 base::SharedMemory::IsHandleValid( 211 stats_table->GetSharedMemoryHandle())) { 212 files_to_register.push_back( 213 FileDescriptorInfo(kStatsTableSharedMemFd, 214 stats_table->GetSharedMemoryHandle())); 215 } 216 #endif 217 218 #if defined(OS_ANDROID) 219 // Android WebView runs in single process, ensure that we never get here 220 // when running in single process mode. 221 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess)); 222 223 GetContentClient()->browser()-> 224 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id, 225 &files_to_register); 226 227 StartChildProcess(cmd_line->argv(), child_process_id, files_to_register, 228 base::Bind(&ChildProcessLauncher::Context::OnChildProcessStarted, 229 this_object, client_thread_id, begin_launch_time)); 230 231 #elif defined(OS_POSIX) 232 base::ProcessHandle handle = base::kNullProcessHandle; 233 // We need to close the client end of the IPC channel to reliably detect 234 // child termination. 235 base::ScopedFD ipcfd_closer(ipcfd); 236 237 #if !defined(OS_MACOSX) 238 GetContentClient()->browser()-> 239 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id, 240 &files_to_register); 241 if (use_zygote) { 242 handle = ZygoteHostImpl::GetInstance()->ForkRequest(cmd_line->argv(), 243 files_to_register, 244 process_type); 245 } else 246 // Fall through to the normal posix case below when we're not zygoting. 247 #endif // !defined(OS_MACOSX) 248 { 249 // Convert FD mapping to FileHandleMappingVector 250 base::FileHandleMappingVector fds_to_map; 251 for (size_t i = 0; i < files_to_register.size(); ++i) { 252 fds_to_map.push_back(std::make_pair( 253 files_to_register[i].fd.fd, 254 files_to_register[i].id + 255 base::GlobalDescriptors::kBaseDescriptor)); 256 } 257 258 #if !defined(OS_MACOSX) 259 if (process_type == switches::kRendererProcess) { 260 const int sandbox_fd = 261 RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); 262 fds_to_map.push_back(std::make_pair( 263 sandbox_fd, 264 GetSandboxFD())); 265 } 266 #endif // defined(OS_MACOSX) 267 268 // Actually launch the app. 269 base::LaunchOptions options; 270 options.environ = env; 271 options.fds_to_remap = &fds_to_map; 272 273 #if defined(OS_MACOSX) 274 // Hold the MachBroker lock for the duration of LaunchProcess. The child 275 // will send its task port to the parent almost immediately after startup. 276 // The Mach message will be delivered to the parent, but updating the 277 // record of the launch will wait until after the placeholder PID is 278 // inserted below. This ensures that while the child process may send its 279 // port to the parent prior to the parent leaving LaunchProcess, the 280 // order in which the record in MachBroker is updated is correct. 281 MachBroker* broker = MachBroker::GetInstance(); 282 broker->GetLock().Acquire(); 283 284 // Make sure the MachBroker is running, and inform it to expect a 285 // check-in from the new process. 286 broker->EnsureRunning(); 287 288 const int bootstrap_sandbox_policy = delegate->GetSandboxType(); 289 if (ShouldEnableBootstrapSandbox() && 290 bootstrap_sandbox_policy != SANDBOX_TYPE_INVALID) { 291 options.replacement_bootstrap_name = 292 GetBootstrapSandbox()->server_bootstrap_name(); 293 GetBootstrapSandbox()->PrepareToForkWithPolicy( 294 bootstrap_sandbox_policy); 295 } 296 #endif // defined(OS_MACOSX) 297 298 bool launched = base::LaunchProcess(*cmd_line, options, &handle); 299 if (!launched) 300 handle = base::kNullProcessHandle; 301 302 #if defined(OS_MACOSX) 303 if (ShouldEnableBootstrapSandbox() && 304 bootstrap_sandbox_policy != SANDBOX_TYPE_INVALID) { 305 GetBootstrapSandbox()->FinishedFork(handle); 306 } 307 308 if (launched) 309 broker->AddPlaceholderForPid(handle); 310 311 // After updating the broker, release the lock and let the child's 312 // messasge be processed on the broker's thread. 313 broker->GetLock().Release(); 314 #endif // defined(OS_MACOSX) 315 } 316 #endif // else defined(OS_POSIX) 317 #if !defined(OS_ANDROID) 318 if (handle) 319 RecordHistograms(begin_launch_time); 320 BrowserThread::PostTask( 321 client_thread_id, FROM_HERE, 322 base::Bind( 323 &Context::Notify, 324 this_object.get(), 325 #if defined(OS_POSIX) && !defined(OS_MACOSX) 326 use_zygote, 327 #endif 328 handle)); 329 #endif // !defined(OS_ANDROID) 330 } 331 332 void Notify( 333 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 334 bool zygote, 335 #endif 336 base::ProcessHandle handle) { 337 #if defined(OS_ANDROID) 338 // Finally close the ipcfd 339 base::ScopedFD ipcfd_closer(ipcfd_); 340 #endif 341 starting_ = false; 342 process_.set_handle(handle); 343 if (!handle) 344 LOG(ERROR) << "Failed to launch child process"; 345 346 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 347 zygote_ = zygote; 348 #endif 349 if (client_) { 350 if (handle) { 351 client_->OnProcessLaunched(); 352 } else { 353 client_->OnProcessLaunchFailed(); 354 } 355 } else { 356 Terminate(); 357 } 358 } 359 360 void Terminate() { 361 if (!process_.handle()) 362 return; 363 364 if (!terminate_child_on_shutdown_) 365 return; 366 367 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So 368 // don't this on the UI/IO threads. 369 BrowserThread::PostTask( 370 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 371 base::Bind( 372 &Context::TerminateInternal, 373 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 374 zygote_, 375 #endif 376 process_.handle())); 377 process_.set_handle(base::kNullProcessHandle); 378 } 379 380 static void SetProcessBackgrounded(base::ProcessHandle handle, 381 bool background) { 382 base::Process process(handle); 383 process.SetProcessBackgrounded(background); 384 #if defined(OS_ANDROID) 385 SetChildProcessInForeground(handle, !background); 386 #endif 387 } 388 389 static void TerminateInternal( 390 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 391 bool zygote, 392 #endif 393 base::ProcessHandle handle) { 394 #if defined(OS_ANDROID) 395 VLOG(0) << "ChromeProcess: Stopping process with handle " << handle; 396 StopChildProcess(handle); 397 #else 398 base::Process process(handle); 399 // Client has gone away, so just kill the process. Using exit code 0 400 // means that UMA won't treat this as a crash. 401 process.Terminate(RESULT_CODE_NORMAL_EXIT); 402 // On POSIX, we must additionally reap the child. 403 #if defined(OS_POSIX) 404 #if !defined(OS_MACOSX) 405 if (zygote) { 406 // If the renderer was created via a zygote, we have to proxy the reaping 407 // through the zygote process. 408 ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(handle); 409 } else 410 #endif // !OS_MACOSX 411 { 412 base::EnsureProcessTerminated(handle); 413 } 414 #endif // OS_POSIX 415 process.Close(); 416 #endif // defined(OS_ANDROID) 417 } 418 419 Client* client_; 420 BrowserThread::ID client_thread_id_; 421 base::Process process_; 422 base::TerminationStatus termination_status_; 423 int exit_code_; 424 bool starting_; 425 // Controls whether the child process should be terminated on browser 426 // shutdown. Default behavior is to terminate the child. 427 bool terminate_child_on_shutdown_; 428 #if defined(OS_ANDROID) 429 // The fd to close after creating the process. 430 int ipcfd_; 431 #elif defined(OS_POSIX) && !defined(OS_MACOSX) 432 bool zygote_; 433 #endif 434 }; 435 436 437 ChildProcessLauncher::ChildProcessLauncher( 438 SandboxedProcessLauncherDelegate* delegate, 439 base::CommandLine* cmd_line, 440 int child_process_id, 441 Client* client) { 442 context_ = new Context(); 443 context_->Launch( 444 delegate, 445 cmd_line, 446 child_process_id, 447 client); 448 } 449 450 ChildProcessLauncher::~ChildProcessLauncher() { 451 context_->ResetClient(); 452 } 453 454 bool ChildProcessLauncher::IsStarting() { 455 return context_->starting_; 456 } 457 458 base::ProcessHandle ChildProcessLauncher::GetHandle() { 459 DCHECK(!context_->starting_); 460 return context_->process_.handle(); 461 } 462 463 base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus( 464 bool known_dead, 465 int* exit_code) { 466 base::ProcessHandle handle = context_->process_.handle(); 467 if (handle == base::kNullProcessHandle) { 468 // Process is already gone, so return the cached termination status. 469 if (exit_code) 470 *exit_code = context_->exit_code_; 471 return context_->termination_status_; 472 } 473 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 474 if (context_->zygote_) { 475 context_->termination_status_ = ZygoteHostImpl::GetInstance()-> 476 GetTerminationStatus(handle, known_dead, &context_->exit_code_); 477 } else if (known_dead) { 478 context_->termination_status_ = 479 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_); 480 } else { 481 #elif defined(OS_MACOSX) 482 if (known_dead) { 483 context_->termination_status_ = 484 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_); 485 } else { 486 #elif defined(OS_ANDROID) 487 if (IsChildProcessOomProtected(handle)) { 488 context_->termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED; 489 } else { 490 #else 491 { 492 #endif 493 context_->termination_status_ = 494 base::GetTerminationStatus(handle, &context_->exit_code_); 495 } 496 497 if (exit_code) 498 *exit_code = context_->exit_code_; 499 500 // POSIX: If the process crashed, then the kernel closed the socket 501 // for it and so the child has already died by the time we get 502 // here. Since GetTerminationStatus called waitpid with WNOHANG, 503 // it'll reap the process. However, if GetTerminationStatus didn't 504 // reap the child (because it was still running), we'll need to 505 // Terminate via ProcessWatcher. So we can't close the handle here. 506 if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING) 507 context_->process_.Close(); 508 509 return context_->termination_status_; 510 } 511 512 void ChildProcessLauncher::SetProcessBackgrounded(bool background) { 513 BrowserThread::PostTask( 514 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 515 base::Bind( 516 &ChildProcessLauncher::Context::SetProcessBackgrounded, 517 GetHandle(), background)); 518 } 519 520 void ChildProcessLauncher::SetTerminateChildOnShutdown( 521 bool terminate_on_shutdown) { 522 if (context_.get()) 523 context_->set_terminate_child_on_shutdown(terminate_on_shutdown); 524 } 525 526 } // namespace content 527