tor-browser

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

process_util_win.cc (14169B)


      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 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
      4 // Use of this source code is governed by a BSD-style license that can be
      5 // found in the LICENSE file.
      6 
      7 #include "base/process_util.h"
      8 
      9 #include <windows.h>
     10 #include <winternl.h>
     11 #include <psapi.h>
     12 #include <io.h>
     13 #ifndef STDOUT_FILENO
     14 #  define STDOUT_FILENO 1
     15 #endif
     16 
     17 #include "base/command_line.h"
     18 #include "base/histogram.h"
     19 #include "base/logging.h"
     20 #include "base/win_util.h"
     21 
     22 #include "mozilla/ipc/LaunchError.h"
     23 #include "mozilla/Result.h"
     24 
     25 #include <algorithm>
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 
     29 namespace {
     30 
     31 typedef BOOL(WINAPI* InitializeProcThreadAttributeListFn)(
     32    LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwAttributeCount,
     33    DWORD dwFlags, PSIZE_T lpSize);
     34 
     35 typedef BOOL(WINAPI* DeleteProcThreadAttributeListFn)(
     36    LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList);
     37 
     38 typedef BOOL(WINAPI* UpdateProcThreadAttributeFn)(
     39    LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwFlags,
     40    DWORD_PTR Attribute, PVOID lpValue, SIZE_T cbSize, PVOID lpPreviousValue,
     41    PSIZE_T lpReturnSize);
     42 
     43 static InitializeProcThreadAttributeListFn InitializeProcThreadAttributeListPtr;
     44 static DeleteProcThreadAttributeListFn DeleteProcThreadAttributeListPtr;
     45 static UpdateProcThreadAttributeFn UpdateProcThreadAttributePtr;
     46 
     47 static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
     48 
     49 }  // namespace
     50 
     51 namespace base {
     52 
     53 ProcessId GetCurrentProcId() { return ::GetCurrentProcessId(); }
     54 
     55 ProcessHandle GetCurrentProcessHandle() { return ::GetCurrentProcess(); }
     56 
     57 bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) {
     58  // TODO(phajdan.jr): Take even more permissions out of this list.
     59  ProcessHandle result =
     60      OpenProcess(PROCESS_DUP_HANDLE | PROCESS_TERMINATE |
     61                      PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
     62                  FALSE, pid);
     63 
     64  if (result == NULL) {
     65    return false;
     66  }
     67 
     68  *handle = result;
     69  return true;
     70 }
     71 
     72 bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) {
     73  ProcessHandle result =
     74      OpenProcess(PROCESS_DUP_HANDLE | PROCESS_TERMINATE |
     75                      PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | SYNCHRONIZE,
     76                  FALSE, pid);
     77 
     78  if (result == NULL) {
     79    return false;
     80  }
     81 
     82  *handle = result;
     83  return true;
     84 }
     85 
     86 void CloseProcessHandle(ProcessHandle process) {
     87  // closing a handle twice on Windows can be catastrophic - after the first
     88  // close the handle value may be reused, so the second close will kill that
     89  // other new handle.
     90  BOOL ok = CloseHandle(process);
     91  DCHECK(ok);
     92 }
     93 
     94 ProcessId GetProcId(ProcessHandle process) {
     95  if (process == base::kInvalidProcessHandle || process == nullptr) {
     96    return 0;
     97  }
     98  // This returns 0 if we have insufficient rights to query the process handle.
     99  // Invalid handles or non-process handles will cause a diagnostic assert.
    100  ProcessId result = GetProcessId(process);
    101 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    102  CHECK(result != 0 || GetLastError() != ERROR_INVALID_HANDLE)
    103  << "process handle = " << process;
    104 #endif
    105  return result;
    106 }
    107 
    108 // from sandbox_policy_base.cc in a later version of the chromium ipc code...
    109 bool IsInheritableHandle(HANDLE handle) {
    110  if (!handle) return false;
    111  if (handle == INVALID_HANDLE_VALUE) return false;
    112  // File handles (FILE_TYPE_DISK) and pipe handles are known to be
    113  // inheritable.  Console handles (FILE_TYPE_CHAR) are not
    114  // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
    115  DWORD handle_type = GetFileType(handle);
    116  return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE;
    117 }
    118 
    119 void LoadThreadAttributeFunctions() {
    120  HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
    121  InitializeProcThreadAttributeListPtr =
    122      reinterpret_cast<InitializeProcThreadAttributeListFn>(
    123          GetProcAddress(kernel32, "InitializeProcThreadAttributeList"));
    124  DeleteProcThreadAttributeListPtr =
    125      reinterpret_cast<DeleteProcThreadAttributeListFn>(
    126          GetProcAddress(kernel32, "DeleteProcThreadAttributeList"));
    127  UpdateProcThreadAttributePtr = reinterpret_cast<UpdateProcThreadAttributeFn>(
    128      GetProcAddress(kernel32, "UpdateProcThreadAttribute"));
    129 }
    130 
    131 // Creates and returns a "thread attribute list" to pass to the child process.
    132 // On return, is a pointer to a THREAD_ATTRIBUTE_LIST or NULL if either the
    133 // functions we need aren't available (eg, XP or earlier) or the functions we
    134 // need failed.
    135 // The result of this function must be passed to FreeThreadAttributeList.
    136 // Note that the pointer to the HANDLE array ends up embedded in the result of
    137 // this function and must stay alive until FreeThreadAttributeList is called,
    138 // hence it is passed in so the owner is the caller of this function.
    139 LPPROC_THREAD_ATTRIBUTE_LIST CreateThreadAttributeList(HANDLE* handlesToInherit,
    140                                                       int handleCount) {
    141  if (!InitializeProcThreadAttributeListPtr ||
    142      !DeleteProcThreadAttributeListPtr || !UpdateProcThreadAttributePtr)
    143    LoadThreadAttributeFunctions();
    144  // shouldn't happen as we are only called for Vista+, but better safe than
    145  // sorry...
    146  if (!InitializeProcThreadAttributeListPtr ||
    147      !DeleteProcThreadAttributeListPtr || !UpdateProcThreadAttributePtr)
    148    return NULL;
    149 
    150  SIZE_T threadAttrSize;
    151  LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;
    152 
    153  if (!(*InitializeProcThreadAttributeListPtr)(NULL, 1, 0, &threadAttrSize) &&
    154      GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    155    goto fail;
    156  lpAttributeList =
    157      reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(threadAttrSize));
    158  if (!lpAttributeList || !(*InitializeProcThreadAttributeListPtr)(
    159                              lpAttributeList, 1, 0, &threadAttrSize))
    160    goto fail;
    161 
    162  if (!(*UpdateProcThreadAttributePtr)(
    163          lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
    164          handlesToInherit, sizeof(handlesToInherit[0]) * handleCount, NULL,
    165          NULL)) {
    166    (*DeleteProcThreadAttributeListPtr)(lpAttributeList);
    167    goto fail;
    168  }
    169  return lpAttributeList;
    170 
    171 fail:
    172  if (lpAttributeList) free(lpAttributeList);
    173  return NULL;
    174 }
    175 
    176 // Frees the data returned by CreateThreadAttributeList.
    177 void FreeThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList) {
    178  // must be impossible to get a NULL DeleteProcThreadAttributeListPtr, as
    179  // we already checked it existed when creating the data we are now freeing.
    180  (*DeleteProcThreadAttributeListPtr)(lpAttributeList);
    181  free(lpAttributeList);
    182 }
    183 
    184 // The next two functions are from chromium/base/environment.cc
    185 //
    186 // Parses a null-terminated input string of an environment block. The key is
    187 // placed into the given string, and the total length of the line, including
    188 // the terminating null, is returned.
    189 static size_t ParseEnvLine(const NativeEnvironmentString::value_type* input,
    190                           NativeEnvironmentString* key) {
    191  // Skip to the equals or end of the string, this is the key.
    192  size_t cur = 0;
    193  while (input[cur] && input[cur] != '=') cur++;
    194  *key = NativeEnvironmentString(&input[0], cur);
    195 
    196  // Now just skip to the end of the string.
    197  while (input[cur]) cur++;
    198  return cur + 1;
    199 }
    200 
    201 std::wstring AlterEnvironment(const wchar_t* env,
    202                              const EnvironmentMap& changes) {
    203  std::wstring result;
    204 
    205  // First copy all unmodified values to the output.
    206  size_t cur_env = 0;
    207  std::wstring key;
    208  while (env[cur_env]) {
    209    const wchar_t* line = &env[cur_env];
    210    size_t line_length = ParseEnvLine(line, &key);
    211 
    212    // Keep only values not specified in the change vector.
    213    EnvironmentMap::const_iterator found_change = changes.find(key);
    214    if (found_change == changes.end()) result.append(line, line_length);
    215 
    216    cur_env += line_length;
    217  }
    218 
    219  // Now append all modified and new values.
    220  for (EnvironmentMap::const_iterator i = changes.begin(); i != changes.end();
    221       ++i) {
    222    if (!i->second.empty()) {
    223      result.append(i->first);
    224      result.push_back('=');
    225      result.append(i->second);
    226      result.push_back(0);
    227    }
    228  }
    229 
    230  // An additional null marks the end of the list. We always need a double-null
    231  // in case nothing was added above.
    232  if (result.empty()) result.push_back(0);
    233  result.push_back(0);
    234  return result;
    235 }
    236 
    237 Result<Ok, LaunchError> LaunchApp(const std::wstring& cmdline,
    238                                  const LaunchOptions& options,
    239                                  ProcessHandle* process_handle) {
    240  // We want to inherit the std handles so dump() statements and assertion
    241  // messages in the child process can be seen - but we *do not* want to
    242  // blindly have all handles inherited.  Vista and later has a technique
    243  // where only specified handles are inherited - so we use this technique.
    244  // If that fails we just don't inherit anything.
    245  DWORD dwCreationFlags = 0;
    246  BOOL bInheritHandles = FALSE;
    247 
    248  // We use a STARTUPINFOEX, but if we can't do the thread attribute thing, we
    249  // just pass the size of a STARTUPINFO.
    250  STARTUPINFOEX startup_info_ex;
    251  ZeroMemory(&startup_info_ex, sizeof(startup_info_ex));
    252  STARTUPINFO& startup_info = startup_info_ex.StartupInfo;
    253  startup_info.cb = sizeof(startup_info);
    254  startup_info.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEOFFFEEDBACK;
    255  startup_info.wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW;
    256 
    257  // Per the comment in CreateThreadAttributeList, lpAttributeList will contain
    258  // a pointer to handlesToInherit, so make sure they have the same lifetime.
    259  LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;
    260  std::vector<HANDLE> handlesToInherit;
    261  for (HANDLE h : options.handles_to_inherit) {
    262    if (SetHandleInformation(h, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) ==
    263        0) {
    264      MOZ_DIAGNOSTIC_CRASH("SetHandleInformation failed");
    265      return Err(
    266          LaunchError::FromWin32Error("SetHandleInformation", GetLastError()));
    267    }
    268    handlesToInherit.push_back(h);
    269  }
    270 
    271  // setup our handle array first - if we end up with no handles that can
    272  // be inherited we can avoid trying to do the ThreadAttributeList dance...
    273  HANDLE stdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
    274  HANDLE stdErr = ::GetStdHandle(STD_ERROR_HANDLE);
    275 
    276  if (IsInheritableHandle(stdOut)) handlesToInherit.push_back(stdOut);
    277  if (stdErr != stdOut && IsInheritableHandle(stdErr))
    278    handlesToInherit.push_back(stdErr);
    279 
    280  if (!handlesToInherit.empty()) {
    281    lpAttributeList = CreateThreadAttributeList(handlesToInherit.data(),
    282                                                handlesToInherit.size());
    283    if (lpAttributeList) {
    284      // it's safe to inherit handles, so arrange for that...
    285      startup_info.cb = sizeof(startup_info_ex);
    286      startup_info.dwFlags |= STARTF_USESTDHANDLES;
    287      startup_info.hStdOutput = stdOut;
    288      startup_info.hStdError = stdErr;
    289      startup_info.hStdInput = INVALID_HANDLE_VALUE;
    290      startup_info_ex.lpAttributeList = lpAttributeList;
    291      dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
    292      bInheritHandles = TRUE;
    293    }
    294  }
    295 
    296  dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
    297  if (options.start_independent) {
    298    dwCreationFlags |= CREATE_BREAKAWAY_FROM_JOB;
    299  }
    300 
    301  LPTCH original_environment = GetEnvironmentStrings();
    302  base::NativeEnvironmentString new_environment =
    303      AlterEnvironment(original_environment, options.env_map);
    304  // Ignore return value? What can we do?
    305  FreeEnvironmentStrings(original_environment);
    306  LPVOID new_env_ptr = (void*)new_environment.data();
    307 
    308  PROCESS_INFORMATION process_info;
    309 
    310  BOOL createdOK = CreateProcess(
    311      NULL, const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL, bInheritHandles,
    312      dwCreationFlags, new_env_ptr, NULL, &startup_info, &process_info);
    313  if (lpAttributeList) FreeThreadAttributeList(lpAttributeList);
    314  if (!createdOK) {
    315    DLOG(WARNING) << "CreateProcess Failed: " << GetLastError();
    316    return Err(LaunchError::FromWin32Error("CreateProcess", GetLastError()));
    317  }
    318 
    319  gProcessLog.print("==> process %d launched child process %d (%S)\n",
    320                    GetCurrentProcId(), process_info.dwProcessId,
    321                    cmdline.c_str());
    322 
    323  // Handles must be closed or they will leak
    324  CloseHandle(process_info.hThread);
    325 
    326  if (options.wait) WaitForSingleObject(process_info.hProcess, INFINITE);
    327 
    328  // If the caller wants the process handle, we won't close it.
    329  if (process_handle) {
    330    *process_handle = process_info.hProcess;
    331  } else {
    332    CloseHandle(process_info.hProcess);
    333  }
    334  return Ok();
    335 }
    336 
    337 Result<Ok, LaunchError> LaunchApp(const CommandLine& cl,
    338                                  const LaunchOptions& options,
    339                                  ProcessHandle* process_handle) {
    340  return LaunchApp(cl.command_line_string(), options, process_handle);
    341 }
    342 
    343 bool KillProcess(ProcessHandle process, int exit_code) {
    344  // INVALID_HANDLE_VALUE is not actually an invalid handle value, but
    345  // instead is the value returned by GetCurrentProcess().  nullptr,
    346  // in contrast, *is* an invalid handle value.  Both values are too
    347  // easy to accidentally try to kill, and neither is legitimately
    348  // used by this function's callers, so reject them.
    349  if (!process || process == INVALID_HANDLE_VALUE) {
    350    CHROMIUM_LOG(WARNING)
    351        << "base::KillProcess refusing to terminate process handle " << process;
    352    return false;
    353  }
    354  bool result = (TerminateProcess(process, exit_code) != FALSE);
    355  if (!result) {
    356    DLOG(ERROR) << "Unable to terminate process: " << GetLastError();
    357  }
    358  return result;
    359 }
    360 
    361 }  // namespace base
    362 
    363 namespace mozilla {
    364 
    365 EnvironmentLog::EnvironmentLog(const char* varname, size_t len) {
    366  wchar_t wvarname[len];
    367  std::copy(varname, varname + len, wvarname);
    368  const wchar_t* e = _wgetenv(wvarname);
    369  if (e && *e) {
    370    fname_ = e;
    371  }
    372 }
    373 
    374 void EnvironmentLog::print(const char* format, ...) {
    375  if (!fname_.size()) return;
    376 
    377  FILE* f;
    378  if (fname_.compare(L"-") == 0) {
    379    f = fdopen(dup(STDOUT_FILENO), "a");
    380  } else {
    381    f = _wfopen(fname_.c_str(), L"a");
    382  }
    383 
    384  if (!f) return;
    385 
    386  va_list a;
    387  va_start(a, format);
    388  vfprintf(f, format, a);
    389  va_end(a);
    390  fclose(f);
    391 }
    392 
    393 }  // namespace mozilla