tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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(&currentStartupInfo);
    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