LauncherProcessWin.cpp (21037B)
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 https://mozilla.org/MPL/2.0/. */ 6 7 #include "LauncherProcessWin.h" 8 9 #include <string.h> 10 11 #include "mozilla/CmdLineAndEnvUtils.h" 12 #include "mozilla/DebugOnly.h" 13 #include "mozilla/glue/Debug.h" 14 #include "mozilla/GeckoArgs.h" 15 #include "mozilla/Maybe.h" 16 #include "mozilla/NativeNt.h" 17 #include "mozilla/SafeMode.h" 18 #include "mozilla/UniquePtr.h" 19 #include "mozilla/WindowsConsole.h" 20 #include "mozilla/WindowsProcessMitigations.h" 21 #include "mozilla/WindowsVersion.h" 22 #include "mozilla/WinHeaderOnlyUtils.h" 23 #include "nsWindowsHelpers.h" 24 25 #include <windows.h> 26 #include <processthreadsapi.h> 27 #include <shlwapi.h> 28 29 #include "DllBlocklistInit.h" 30 #include "ErrorHandler.h" 31 #include "LaunchUnelevated.h" 32 #include "ProcThreadAttributes.h" 33 #include "../BrowserDefines.h" 34 35 #if defined(MOZ_LAUNCHER_PROCESS) 36 # include "mozilla/LauncherRegistryInfo.h" 37 # include "SameBinary.h" 38 #endif // defined(MOZ_LAUNCHER_PROCESS) 39 40 #if defined(MOZ_SANDBOX) 41 # include "mozilla/sandboxing/SandboxInitialization.h" 42 #endif 43 44 namespace mozilla { 45 // "const" because nothing in this process modifies it. 46 // "volatile" because something in another process may. 47 const volatile DeelevationStatus gDeelevationStatus = 48 DeelevationStatus::DefaultStaticValue; 49 } // namespace mozilla 50 51 /** 52 * At this point the child process has been created in a suspended state. Any 53 * additional startup work (eg, blocklist setup) should go here. 54 * 55 * @return Ok if browser startup should proceed 56 */ 57 static mozilla::LauncherVoidResult PostCreationSetup( 58 const wchar_t* aFullImagePath, HANDLE aChildProcess, 59 HANDLE aChildMainThread, mozilla::DeelevationStatus aDStatus, 60 const bool aIsSafeMode, const bool aDisableDynamicBlocklist, 61 mozilla::Maybe<std::wstring> aBlocklistFileName) { 62 /* scope for txManager */ { 63 mozilla::nt::CrossExecTransferManager txManager(aChildProcess); 64 if (!txManager) { 65 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT); 66 } 67 68 using mozilla::gDeelevationStatus; 69 70 void* targetAddress = (LPVOID)&gDeelevationStatus; 71 72 auto const guard = txManager.Protect( 73 targetAddress, sizeof(gDeelevationStatus), PAGE_READWRITE); 74 75 mozilla::LauncherVoidResult result = 76 txManager.Transfer(targetAddress, &aDStatus, sizeof(aDStatus)); 77 if (result.isErr()) { 78 return result; 79 } 80 } 81 82 return mozilla::InitializeDllBlocklistOOPFromLauncher( 83 aFullImagePath, aChildProcess, aDisableDynamicBlocklist, 84 aBlocklistFileName); 85 } 86 87 /** 88 * Create a new Job object and assign |aProcess| to it. If something fails 89 * in this function, we return nullptr but continue without recording 90 * a launcher failure because it's not a critical problem to launch 91 * the browser process. 92 */ 93 static nsReturnRef<HANDLE> CreateJobAndAssignProcess(HANDLE aProcess) { 94 nsAutoHandle empty; 95 nsAutoHandle job(::CreateJobObjectW(nullptr, nullptr)); 96 97 // Set JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK to put only browser process 98 // into a job without putting children of browser process into the job. 99 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo = {}; 100 jobInfo.BasicLimitInformation.LimitFlags = 101 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; 102 if (!::SetInformationJobObject(job.get(), JobObjectExtendedLimitInformation, 103 &jobInfo, sizeof(jobInfo))) { 104 return empty.out(); 105 } 106 107 if (!::AssignProcessToJobObject(job.get(), aProcess)) { 108 return empty.out(); 109 } 110 111 return job.out(); 112 } 113 114 enum class VCRuntimeDLLDir : bool { 115 Application, 116 System, 117 }; 118 static bool GetMSVCP140VersionInfo(VCRuntimeDLLDir aDir, 119 uint64_t& aOutVersion) { 120 wchar_t dllPath[MAX_PATH]; 121 if (aDir == VCRuntimeDLLDir::Application) { 122 DWORD size = ::GetModuleFileNameW(nullptr, dllPath, MAX_PATH); 123 if (!size || 124 (size == MAX_PATH && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) || 125 !::PathRemoveFileSpecW(dllPath)) { 126 return false; 127 } 128 } else { 129 MOZ_ASSERT(aDir == VCRuntimeDLLDir::System); 130 UINT size = ::GetSystemDirectoryW(dllPath, MAX_PATH); 131 if (!size || size >= MAX_PATH) { 132 return false; 133 } 134 } 135 136 if (!::PathAppendW(dllPath, L"msvcp140.dll")) { 137 return false; 138 } 139 HMODULE crt = 140 ::LoadLibraryExW(dllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE); 141 if (!crt) { 142 return false; 143 } 144 145 mozilla::nt::PEHeaders headers{crt}; 146 bool result = headers.GetVersionInfo(aOutVersion); 147 ::FreeLibrary(crt); 148 return result; 149 } 150 151 /** 152 * Choose whether we want to favor loading DLLs from the system directory over 153 * the application directory. This choice automatically propagates to all child 154 * processes. In particular, it determines whether child processes will load 155 * Visual C++ runtime DLLs from the system or the application directory at 156 * startup. 157 * 158 * Whenever possible, we want all processes to favor loading DLLs from the 159 * system directory. But if old Visual C++ runtime DLLs are installed 160 * system-wide, then we must favor loading from the application directory 161 * instead to ensure compatibility, at least during startup. So in this case we 162 * only apply the delayed variant of the mitigation and only in sandboxed 163 * processes, which is the best compromise (see SandboxBroker::LaunchApp). 164 * 165 * This function is called from the launcher process *and* the browser process. 166 * This is because if the launcher process is disabled, we still want the 167 * browser process to go through this code so that it enforces the correct 168 * choice for itself and for child processes. 169 */ 170 static void EnablePreferLoadFromSystem32IfCompatible() { 171 // We may already have the mitigation if we are the browser process and we 172 // inherited it from the launcher process. 173 if (!mozilla::IsPreferLoadFromSystem32Available() || 174 mozilla::IsPreferLoadFromSystem32Enabled()) { 175 return; 176 } 177 178 // Only bail out if (1) there is a conflict because the two DLLs exist *and* 179 // (2) the version of the system DLL is problematic. 180 uint64_t systemDirVersion = 0, appDirVersion = 0; 181 if (GetMSVCP140VersionInfo(VCRuntimeDLLDir::System, systemDirVersion) && 182 GetMSVCP140VersionInfo(VCRuntimeDLLDir::Application, appDirVersion) && 183 systemDirVersion < appDirVersion) { 184 return; 185 } 186 187 mozilla::DebugOnly<bool> setOk = mozilla::EnablePreferLoadFromSystem32(); 188 MOZ_ASSERT(setOk); 189 } 190 191 /** 192 * Any mitigation policies that should be set on the browser process should go 193 * here. 194 */ 195 static void SetMitigationPolicies(mozilla::ProcThreadAttributes& aAttrs, 196 const bool aIsSafeMode) { 197 // Note: Do *not* handle IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON here. For this 198 // mitigation we rely on EnablePreferLoadFromSystem32IfCompatible(). 199 // The launcher process or the browser process will choose whether we 200 // want to apply the mitigation or not, and child processes will 201 // automatically inherit that choice. 202 203 #if defined(_M_ARM64) 204 // Disable CFG on older versions of ARM64 Windows to avoid a crash in COM. 205 if (!mozilla::IsWin10Sep2018UpdateOrLater()) { 206 aAttrs.AddMitigationPolicy( 207 PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF); 208 } 209 #endif // defined(_M_ARM64) 210 } 211 212 static mozilla::LauncherFlags ProcessCmdLine(int& aArgc, wchar_t* aArgv[]) { 213 mozilla::LauncherFlags result = mozilla::LauncherFlags::eNone; 214 215 if (mozilla::CheckArg(aArgc, aArgv, "wait-for-browser", nullptr, 216 mozilla::CheckArgFlag::RemoveArg) == 217 mozilla::ARG_FOUND || 218 mozilla::CheckArg(aArgc, aArgv, "marionette", nullptr, 219 mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND || 220 mozilla::CheckArg(aArgc, aArgv, "backgroundtask", nullptr, 221 mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND || 222 mozilla::CheckArg(aArgc, aArgv, "headless", nullptr, 223 mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND || 224 mozilla::CheckArg(aArgc, aArgv, "remote-debugging-port", nullptr, 225 mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND || 226 mozilla::EnvHasValue("MOZ_AUTOMATION") || 227 mozilla::EnvHasValue("MOZ_HEADLESS")) { 228 result |= mozilla::LauncherFlags::eWaitForBrowser; 229 } 230 231 if (mozilla::CheckArg(aArgc, aArgv, "no-deelevate") == mozilla::ARG_FOUND) { 232 result |= mozilla::LauncherFlags::eNoDeelevate; 233 } 234 235 if (mozilla::CheckArg(aArgc, aArgv, ATTEMPTING_DEELEVATION_FLAG) == 236 mozilla::ARG_FOUND) { 237 result |= mozilla::LauncherFlags::eDeelevating; 238 } 239 240 return result; 241 } 242 243 static void MaybeBreakForBrowserDebugging() { 244 if (mozilla::EnvHasValue("MOZ_DEBUG_BROWSER_PROCESS")) { 245 ::DebugBreak(); 246 return; 247 } 248 249 const wchar_t* pauseLenS = _wgetenv(L"MOZ_DEBUG_BROWSER_PAUSE"); 250 if (!pauseLenS || !(*pauseLenS)) { 251 return; 252 } 253 254 DWORD pauseLenMs = wcstoul(pauseLenS, nullptr, 10) * 1000; 255 printf_stderr("\n\nBROWSERBROWSERBROWSERBROWSER\n debug me @ %lu\n\n", 256 ::GetCurrentProcessId()); 257 ::Sleep(pauseLenMs); 258 } 259 260 static bool DoLauncherProcessChecks(int& argc, wchar_t** argv) { 261 // NB: We run all tests in this function instead of returning early in order 262 // to ensure that all side effects take place, such as clearing environment 263 // variables. 264 bool result = false; 265 266 #if defined(MOZ_LAUNCHER_PROCESS) 267 // We still prefer to compare file ids. Comparing NT paths i.e. passing 268 // CompareNtPathsOnly to IsSameBinaryAsParentProcess is much faster, but 269 // we're not 100% sure that NT path comparison perfectly prevents the 270 // launching loop of the launcher process. 271 mozilla::LauncherResult<bool> isSame = mozilla::IsSameBinaryAsParentProcess(); 272 if (isSame.isOk()) { 273 result = !isSame.unwrap(); 274 } else { 275 HandleLauncherError(isSame.unwrapErr()); 276 } 277 #endif // defined(MOZ_LAUNCHER_PROCESS) 278 279 if (mozilla::EnvHasValue("MOZ_LAUNCHER_PROCESS")) { 280 mozilla::SaveToEnv("MOZ_LAUNCHER_PROCESS="); 281 result = true; 282 } 283 284 result |= 285 mozilla::CheckArg(argc, argv, "launcher", nullptr, 286 mozilla::CheckArgFlag::RemoveArg) == mozilla::ARG_FOUND; 287 288 return result; 289 } 290 291 #if defined(MOZ_LAUNCHER_PROCESS) 292 static mozilla::Maybe<bool> RunAsLauncherProcess( 293 mozilla::LauncherRegistryInfo& aRegInfo, int& argc, wchar_t** argv) { 294 #else 295 static mozilla::Maybe<bool> RunAsLauncherProcess(int& argc, wchar_t** argv) { 296 #endif // defined(MOZ_LAUNCHER_PROCESS) 297 bool runAsLauncher = DoLauncherProcessChecks(argc, argv); 298 299 #if defined(MOZ_LAUNCHER_PROCESS) 300 bool forceLauncher = 301 runAsLauncher && 302 mozilla::CheckArg(argc, argv, "force-launcher", nullptr, 303 mozilla::CheckArgFlag::RemoveArg) == mozilla::ARG_FOUND; 304 305 mozilla::LauncherRegistryInfo::ProcessType desiredType = 306 runAsLauncher ? mozilla::LauncherRegistryInfo::ProcessType::Launcher 307 : mozilla::LauncherRegistryInfo::ProcessType::Browser; 308 309 mozilla::LauncherRegistryInfo::CheckOption checkOption = 310 forceLauncher ? mozilla::LauncherRegistryInfo::CheckOption::Force 311 : mozilla::LauncherRegistryInfo::CheckOption::Default; 312 313 mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> 314 runAsType = aRegInfo.Check(desiredType, checkOption); 315 316 if (runAsType.isErr()) { 317 mozilla::HandleLauncherError(runAsType); 318 return mozilla::Nothing(); 319 } 320 321 runAsLauncher = runAsType.unwrap() == 322 mozilla::LauncherRegistryInfo::ProcessType::Launcher; 323 #endif // defined(MOZ_LAUNCHER_PROCESS) 324 325 if (!runAsLauncher) { 326 // In this case, we will be proceeding to run as the browser. 327 // We should check MOZ_DEBUG_BROWSER_* env vars. 328 MaybeBreakForBrowserDebugging(); 329 } 330 331 return mozilla::Some(runAsLauncher); 332 } 333 334 namespace mozilla { 335 336 Maybe<int> LauncherMain(int& argc, wchar_t* argv[]) { 337 EnsureBrowserCommandlineSafe(argc, argv); 338 339 // return fast when we're a child process. 340 // (The remainder of this function has some side effects that are 341 // undesirable for content processes) 342 if (mozilla::CheckArg(argc, argv, "contentproc", nullptr, 343 mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND) { 344 // A child process should not instantiate LauncherRegistryInfo. 345 return Nothing(); 346 } 347 348 // Called from the launcher process *and* the browser process. 349 EnablePreferLoadFromSystem32IfCompatible(); 350 351 #if defined(MOZ_LAUNCHER_PROCESS) 352 LauncherRegistryInfo regInfo; 353 Maybe<bool> runAsLauncher = RunAsLauncherProcess(regInfo, argc, argv); 354 LauncherResult<std::wstring> blocklistFileNameResult = 355 regInfo.GetBlocklistFileName(); 356 Maybe<std::wstring> blocklistFileName = 357 blocklistFileNameResult.isOk() ? Some(blocklistFileNameResult.unwrap()) 358 : Nothing(); 359 #else 360 Maybe<bool> runAsLauncher = RunAsLauncherProcess(argc, argv); 361 Maybe<std::wstring> blocklistFileName = Nothing(); 362 #endif // defined(MOZ_LAUNCHER_PROCESS) 363 if (!runAsLauncher || !runAsLauncher.value()) { 364 #if defined(MOZ_LAUNCHER_PROCESS) 365 // Update the registry as Browser 366 LauncherVoidResult commitResult = regInfo.Commit(); 367 if (commitResult.isErr()) { 368 mozilla::HandleLauncherError(commitResult); 369 } 370 #endif // defined(MOZ_LAUNCHER_PROCESS) 371 return Nothing(); 372 } 373 374 #if defined(MOZ_SANDBOX) 375 // Ensure the relevant mitigations are enforced. 376 mozilla::sandboxing::ApplyParentProcessMitigations(); 377 #endif 378 379 mozilla::UseParentConsole(); 380 381 if (!SetArgv0ToFullBinaryPath(argv)) { 382 HandleLauncherError(LAUNCHER_ERROR_GENERIC()); 383 return Nothing(); 384 } 385 386 LauncherFlags flags = ProcessCmdLine(argc, argv); 387 388 nsAutoHandle mediumIlToken; 389 LauncherResult<ElevationState> elevationState = 390 GetElevationState(argv[0], flags, mediumIlToken); 391 if (elevationState.isErr()) { 392 HandleLauncherError(elevationState); 393 return Nothing(); 394 } 395 396 // Distill deelevation status, and/or attempt to perform launcher deelevation 397 // via an indirect relaunch. 398 DeelevationStatus deelevationStatus = DeelevationStatus::Unknown; 399 if (mediumIlToken.get()) { 400 // Rather than indirectly relaunch the launcher, we'll attempt to directly 401 // launch the main process with a reduced-privilege security token. 402 deelevationStatus = DeelevationStatus::PartiallyDeelevated; 403 } else if (elevationState.unwrap() == ElevationState::eElevated) { 404 if (flags & LauncherFlags::eWaitForBrowser) { 405 // An indirect relaunch won't provide a process-handle to block on, 406 // so we have to continue onwards with this process. 407 deelevationStatus = DeelevationStatus::DeelevationProhibited; 408 } else if (flags & LauncherFlags::eNoDeelevate) { 409 // Our invoker (hopefully, the user) has explicitly requested that the 410 // launcher not deelevate itself. 411 deelevationStatus = DeelevationStatus::DeelevationProhibited; 412 } else if (flags & LauncherFlags::eDeelevating) { 413 // We've already tried to deelevate, to no effect. Continue onward. 414 deelevationStatus = DeelevationStatus::UnsuccessfullyDeelevated; 415 } else { 416 // Otherwise, attempt to relaunch the launcher process itself via the 417 // shell, which hopefully will not be elevated. (But see bug 1733821.) 418 LauncherVoidResult launchedUnelevated = LaunchUnelevated(argc, argv); 419 if (launchedUnelevated.isErr()) { 420 // On failure, don't even try for a launcher process. Continue onwards 421 // in this one. (TODO: why? This isn't technically fatal...) 422 HandleLauncherError(launchedUnelevated); 423 return Nothing(); 424 } 425 // Otherwise, tell our caller to exit with a success code. 426 return Some(0); 427 } 428 } else if (elevationState.unwrap() == ElevationState::eNormalUser) { 429 if (flags & LauncherFlags::eDeelevating) { 430 // Deelevation appears to have been successful! 431 deelevationStatus = DeelevationStatus::SuccessfullyDeelevated; 432 } else { 433 // We haven't done anything and we don't need to. 434 deelevationStatus = DeelevationStatus::StartedUnprivileged; 435 } 436 } else { 437 // Some other elevation state with no medium-integrity token. 438 // (This should probably not happen.) 439 deelevationStatus = DeelevationStatus::Unknown; 440 } 441 442 #if defined(MOZ_LAUNCHER_PROCESS) 443 // Update the registry as Launcher 444 LauncherVoidResult commitResult = regInfo.Commit(); 445 if (commitResult.isErr()) { 446 mozilla::HandleLauncherError(commitResult); 447 return Nothing(); 448 } 449 #endif // defined(MOZ_LAUNCHER_PROCESS) 450 451 // Now proceed with setting up the parameters for process creation 452 UniquePtr<wchar_t[]> cmdLine(MakeCommandLine(argc, argv)); 453 if (!cmdLine) { 454 HandleLauncherError(LAUNCHER_ERROR_GENERIC()); 455 return Nothing(); 456 } 457 458 const Maybe<bool> isSafeMode = 459 IsSafeModeRequested(argc, argv, SafeModeFlag::NoKeyPressCheck); 460 if (!isSafeMode) { 461 HandleLauncherError(LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_PARAMETER)); 462 return Nothing(); 463 } 464 465 ProcThreadAttributes attrs; 466 SetMitigationPolicies(attrs, isSafeMode.value()); 467 468 HANDLE stdHandles[] = {::GetStdHandle(STD_INPUT_HANDLE), 469 ::GetStdHandle(STD_OUTPUT_HANDLE), 470 ::GetStdHandle(STD_ERROR_HANDLE)}; 471 472 attrs.AddInheritableHandles(stdHandles); 473 474 DWORD creationFlags = CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT; 475 476 STARTUPINFOEXW siex; 477 LauncherResult<bool> attrsOk = attrs.AssignTo(siex); 478 if (attrsOk.isErr()) { 479 HandleLauncherError(attrsOk); 480 return Nothing(); 481 } 482 483 BOOL inheritHandles = FALSE; 484 485 if (attrsOk.unwrap()) { 486 creationFlags |= EXTENDED_STARTUPINFO_PRESENT; 487 488 if (attrs.HasInheritableHandles()) { 489 siex.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; 490 siex.StartupInfo.hStdInput = stdHandles[0]; 491 siex.StartupInfo.hStdOutput = stdHandles[1]; 492 siex.StartupInfo.hStdError = stdHandles[2]; 493 494 // Since attrsOk == true, we have successfully set the handle inheritance 495 // whitelist policy, so only the handles added to attrs will be inherited. 496 inheritHandles = TRUE; 497 } 498 } 499 500 // Pass on the path of the shortcut used to launch this process, if any. 501 STARTUPINFOW currentStartupInfo = {.cb = sizeof(STARTUPINFOW)}; 502 GetStartupInfoW(¤tStartupInfo); 503 if ((currentStartupInfo.dwFlags & STARTF_TITLEISLINKNAME) && 504 currentStartupInfo.lpTitle) { 505 siex.StartupInfo.dwFlags |= STARTF_TITLEISLINKNAME; 506 siex.StartupInfo.lpTitle = currentStartupInfo.lpTitle; 507 } 508 509 PROCESS_INFORMATION pi = {}; 510 BOOL createOk; 511 512 if (mediumIlToken.get()) { 513 createOk = 514 ::CreateProcessAsUserW(mediumIlToken.get(), argv[0], cmdLine.get(), 515 nullptr, nullptr, inheritHandles, creationFlags, 516 nullptr, nullptr, &siex.StartupInfo, &pi); 517 } else { 518 createOk = ::CreateProcessW(argv[0], cmdLine.get(), nullptr, nullptr, 519 inheritHandles, creationFlags, nullptr, nullptr, 520 &siex.StartupInfo, &pi); 521 } 522 523 if (!createOk) { 524 HandleLauncherError(LAUNCHER_ERROR_FROM_LAST()); 525 return Nothing(); 526 } 527 528 nsAutoHandle process(pi.hProcess); 529 nsAutoHandle mainThread(pi.hThread); 530 531 nsAutoHandle job; 532 if (flags & LauncherFlags::eWaitForBrowser) { 533 job = CreateJobAndAssignProcess(process.get()); 534 } 535 536 bool disableDynamicBlocklist = IsDynamicBlocklistDisabled( 537 isSafeMode.value(), 538 mozilla::CheckArg( 539 argc, argv, mozilla::geckoargs::sDisableDynamicDllBlocklist.sMatch, 540 nullptr, mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND); 541 LauncherVoidResult setupResult = PostCreationSetup( 542 argv[0], process.get(), mainThread.get(), deelevationStatus, 543 isSafeMode.value(), disableDynamicBlocklist, blocklistFileName); 544 if (setupResult.isErr()) { 545 HandleLauncherError(setupResult); 546 ::TerminateProcess(process.get(), 1); 547 return Nothing(); 548 } 549 550 if (::ResumeThread(mainThread.get()) == static_cast<DWORD>(-1)) { 551 HandleLauncherError(LAUNCHER_ERROR_FROM_LAST()); 552 ::TerminateProcess(process.get(), 1); 553 return Nothing(); 554 } 555 556 if (flags & LauncherFlags::eWaitForBrowser) { 557 DWORD exitCode; 558 if (::WaitForSingleObject(process.get(), INFINITE) == WAIT_OBJECT_0 && 559 ::GetExitCodeProcess(process.get(), &exitCode)) { 560 // Propagate the browser process's exit code as our exit code. 561 return Some(static_cast<int>(exitCode)); 562 } 563 } else { 564 const DWORD timeout = 565 ::IsDebuggerPresent() ? INFINITE : kWaitForInputIdleTimeoutMS; 566 567 // Keep the current process around until the callback process has created 568 // its message queue, to avoid the launched process's windows being forced 569 // into the background. 570 mozilla::WaitForInputIdle(process.get(), timeout); 571 } 572 573 return Some(0); 574 } 575 576 } // namespace mozilla