summaryrefslogtreecommitdiffstats
path: root/ipc/chromium/src/base/process_util_win.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /ipc/chromium/src/base/process_util_win.cc
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ipc/chromium/src/base/process_util_win.cc')
-rw-r--r--ipc/chromium/src/base/process_util_win.cc392
1 files changed, 392 insertions, 0 deletions
diff --git a/ipc/chromium/src/base/process_util_win.cc b/ipc/chromium/src/base/process_util_win.cc
new file mode 100644
index 0000000000..338917ef98
--- /dev/null
+++ b/ipc/chromium/src/base/process_util_win.cc
@@ -0,0 +1,392 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process_util.h"
+
+#include <windows.h>
+#include <winternl.h>
+#include <psapi.h>
+#include <io.h>
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif
+
+#include "base/command_line.h"
+#include "base/histogram.h"
+#include "base/logging.h"
+#include "base/win_util.h"
+
+#include "mozilla/ipc/LaunchError.h"
+#include "mozilla/Result.h"
+
+#include <algorithm>
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace {
+
+typedef BOOL(WINAPI* InitializeProcThreadAttributeListFn)(
+ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwAttributeCount,
+ DWORD dwFlags, PSIZE_T lpSize);
+
+typedef BOOL(WINAPI* DeleteProcThreadAttributeListFn)(
+ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList);
+
+typedef BOOL(WINAPI* UpdateProcThreadAttributeFn)(
+ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwFlags,
+ DWORD_PTR Attribute, PVOID lpValue, SIZE_T cbSize, PVOID lpPreviousValue,
+ PSIZE_T lpReturnSize);
+
+static InitializeProcThreadAttributeListFn InitializeProcThreadAttributeListPtr;
+static DeleteProcThreadAttributeListFn DeleteProcThreadAttributeListPtr;
+static UpdateProcThreadAttributeFn UpdateProcThreadAttributePtr;
+
+static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
+
+} // namespace
+
+namespace base {
+
+ProcessId GetCurrentProcId() { return ::GetCurrentProcessId(); }
+
+ProcessHandle GetCurrentProcessHandle() { return ::GetCurrentProcess(); }
+
+bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) {
+ // TODO(phajdan.jr): Take even more permissions out of this list.
+ ProcessHandle result =
+ OpenProcess(PROCESS_DUP_HANDLE | PROCESS_TERMINATE |
+ PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
+ FALSE, pid);
+
+ if (result == NULL) {
+ return false;
+ }
+
+ *handle = result;
+ return true;
+}
+
+bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) {
+ ProcessHandle result =
+ OpenProcess(PROCESS_DUP_HANDLE | PROCESS_TERMINATE |
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | SYNCHRONIZE,
+ FALSE, pid);
+
+ if (result == NULL) {
+ return false;
+ }
+
+ *handle = result;
+ return true;
+}
+
+void CloseProcessHandle(ProcessHandle process) {
+ // closing a handle twice on Windows can be catastrophic - after the first
+ // close the handle value may be reused, so the second close will kill that
+ // other new handle.
+ BOOL ok = CloseHandle(process);
+ DCHECK(ok);
+}
+
+ProcessId GetProcId(ProcessHandle process) {
+ if (process == base::kInvalidProcessHandle || process == nullptr) {
+ return 0;
+ }
+ // This returns 0 if we have insufficient rights to query the process handle.
+ // Invalid handles or non-process handles will cause a diagnostic assert.
+ ProcessId result = GetProcessId(process);
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ CHECK(result != 0 || GetLastError() != ERROR_INVALID_HANDLE)
+ << "process handle = " << process;
+#endif
+ return result;
+}
+
+// from sandbox_policy_base.cc in a later version of the chromium ipc code...
+bool IsInheritableHandle(HANDLE handle) {
+ if (!handle) return false;
+ if (handle == INVALID_HANDLE_VALUE) return false;
+ // File handles (FILE_TYPE_DISK) and pipe handles are known to be
+ // inheritable. Console handles (FILE_TYPE_CHAR) are not
+ // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
+ DWORD handle_type = GetFileType(handle);
+ return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE;
+}
+
+void LoadThreadAttributeFunctions() {
+ HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
+ InitializeProcThreadAttributeListPtr =
+ reinterpret_cast<InitializeProcThreadAttributeListFn>(
+ GetProcAddress(kernel32, "InitializeProcThreadAttributeList"));
+ DeleteProcThreadAttributeListPtr =
+ reinterpret_cast<DeleteProcThreadAttributeListFn>(
+ GetProcAddress(kernel32, "DeleteProcThreadAttributeList"));
+ UpdateProcThreadAttributePtr = reinterpret_cast<UpdateProcThreadAttributeFn>(
+ GetProcAddress(kernel32, "UpdateProcThreadAttribute"));
+}
+
+// Creates and returns a "thread attribute list" to pass to the child process.
+// On return, is a pointer to a THREAD_ATTRIBUTE_LIST or NULL if either the
+// functions we need aren't available (eg, XP or earlier) or the functions we
+// need failed.
+// The result of this function must be passed to FreeThreadAttributeList.
+// Note that the pointer to the HANDLE array ends up embedded in the result of
+// this function and must stay alive until FreeThreadAttributeList is called,
+// hence it is passed in so the owner is the caller of this function.
+LPPROC_THREAD_ATTRIBUTE_LIST CreateThreadAttributeList(HANDLE* handlesToInherit,
+ int handleCount) {
+ if (!InitializeProcThreadAttributeListPtr ||
+ !DeleteProcThreadAttributeListPtr || !UpdateProcThreadAttributePtr)
+ LoadThreadAttributeFunctions();
+ // shouldn't happen as we are only called for Vista+, but better safe than
+ // sorry...
+ if (!InitializeProcThreadAttributeListPtr ||
+ !DeleteProcThreadAttributeListPtr || !UpdateProcThreadAttributePtr)
+ return NULL;
+
+ SIZE_T threadAttrSize;
+ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;
+
+ if (!(*InitializeProcThreadAttributeListPtr)(NULL, 1, 0, &threadAttrSize) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ goto fail;
+ lpAttributeList =
+ reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(threadAttrSize));
+ if (!lpAttributeList || !(*InitializeProcThreadAttributeListPtr)(
+ lpAttributeList, 1, 0, &threadAttrSize))
+ goto fail;
+
+ if (!(*UpdateProcThreadAttributePtr)(
+ lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ handlesToInherit, sizeof(handlesToInherit[0]) * handleCount, NULL,
+ NULL)) {
+ (*DeleteProcThreadAttributeListPtr)(lpAttributeList);
+ goto fail;
+ }
+ return lpAttributeList;
+
+fail:
+ if (lpAttributeList) free(lpAttributeList);
+ return NULL;
+}
+
+// Frees the data returned by CreateThreadAttributeList.
+void FreeThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList) {
+ // must be impossible to get a NULL DeleteProcThreadAttributeListPtr, as
+ // we already checked it existed when creating the data we are now freeing.
+ (*DeleteProcThreadAttributeListPtr)(lpAttributeList);
+ free(lpAttributeList);
+}
+
+// The next two functions are from chromium/base/environment.cc
+//
+// Parses a null-terminated input string of an environment block. The key is
+// placed into the given string, and the total length of the line, including
+// the terminating null, is returned.
+static size_t ParseEnvLine(const NativeEnvironmentString::value_type* input,
+ NativeEnvironmentString* key) {
+ // Skip to the equals or end of the string, this is the key.
+ size_t cur = 0;
+ while (input[cur] && input[cur] != '=') cur++;
+ *key = NativeEnvironmentString(&input[0], cur);
+
+ // Now just skip to the end of the string.
+ while (input[cur]) cur++;
+ return cur + 1;
+}
+
+std::wstring AlterEnvironment(const wchar_t* env,
+ const EnvironmentMap& changes) {
+ std::wstring result;
+
+ // First copy all unmodified values to the output.
+ size_t cur_env = 0;
+ std::wstring key;
+ while (env[cur_env]) {
+ const wchar_t* line = &env[cur_env];
+ size_t line_length = ParseEnvLine(line, &key);
+
+ // Keep only values not specified in the change vector.
+ EnvironmentMap::const_iterator found_change = changes.find(key);
+ if (found_change == changes.end()) result.append(line, line_length);
+
+ cur_env += line_length;
+ }
+
+ // Now append all modified and new values.
+ for (EnvironmentMap::const_iterator i = changes.begin(); i != changes.end();
+ ++i) {
+ if (!i->second.empty()) {
+ result.append(i->first);
+ result.push_back('=');
+ result.append(i->second);
+ result.push_back(0);
+ }
+ }
+
+ // An additional null marks the end of the list. We always need a double-null
+ // in case nothing was added above.
+ if (result.empty()) result.push_back(0);
+ result.push_back(0);
+ return result;
+}
+
+Result<Ok, LaunchError> LaunchApp(const std::wstring& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ // We want to inherit the std handles so dump() statements and assertion
+ // messages in the child process can be seen - but we *do not* want to
+ // blindly have all handles inherited. Vista and later has a technique
+ // where only specified handles are inherited - so we use this technique.
+ // If that fails we just don't inherit anything.
+ DWORD dwCreationFlags = 0;
+ BOOL bInheritHandles = FALSE;
+
+ // We use a STARTUPINFOEX, but if we can't do the thread attribute thing, we
+ // just pass the size of a STARTUPINFO.
+ STARTUPINFOEX startup_info_ex;
+ ZeroMemory(&startup_info_ex, sizeof(startup_info_ex));
+ STARTUPINFO& startup_info = startup_info_ex.StartupInfo;
+ startup_info.cb = sizeof(startup_info);
+ startup_info.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEOFFFEEDBACK;
+ startup_info.wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW;
+
+ // Per the comment in CreateThreadAttributeList, lpAttributeList will contain
+ // a pointer to handlesToInherit, so make sure they have the same lifetime.
+ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;
+ std::vector<HANDLE> handlesToInherit;
+ for (HANDLE h : options.handles_to_inherit) {
+ if (SetHandleInformation(h, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) ==
+ 0) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "SetHandleInformation failed");
+ return Err(LaunchError("SetHandleInformation", GetLastError()));
+ }
+ handlesToInherit.push_back(h);
+ }
+
+ // setup our handle array first - if we end up with no handles that can
+ // be inherited we can avoid trying to do the ThreadAttributeList dance...
+ HANDLE stdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
+ HANDLE stdErr = ::GetStdHandle(STD_ERROR_HANDLE);
+
+ if (IsInheritableHandle(stdOut)) handlesToInherit.push_back(stdOut);
+ if (stdErr != stdOut && IsInheritableHandle(stdErr))
+ handlesToInherit.push_back(stdErr);
+
+ if (!handlesToInherit.empty()) {
+ lpAttributeList = CreateThreadAttributeList(handlesToInherit.data(),
+ handlesToInherit.size());
+ if (lpAttributeList) {
+ // it's safe to inherit handles, so arrange for that...
+ startup_info.cb = sizeof(startup_info_ex);
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+ startup_info.hStdOutput = stdOut;
+ startup_info.hStdError = stdErr;
+ startup_info.hStdInput = INVALID_HANDLE_VALUE;
+ startup_info_ex.lpAttributeList = lpAttributeList;
+ dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
+ bInheritHandles = TRUE;
+ }
+ }
+
+ dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
+ if (options.start_independent) {
+ dwCreationFlags |= CREATE_BREAKAWAY_FROM_JOB;
+ }
+
+ LPTCH original_environment = GetEnvironmentStrings();
+ base::NativeEnvironmentString new_environment =
+ AlterEnvironment(original_environment, options.env_map);
+ // Ignore return value? What can we do?
+ FreeEnvironmentStrings(original_environment);
+ LPVOID new_env_ptr = (void*)new_environment.data();
+
+ PROCESS_INFORMATION process_info;
+
+ BOOL createdOK = CreateProcess(
+ NULL, const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL, bInheritHandles,
+ dwCreationFlags, new_env_ptr, NULL, &startup_info, &process_info);
+ if (lpAttributeList) FreeThreadAttributeList(lpAttributeList);
+ if (!createdOK) {
+ DLOG(WARNING) << "CreateProcess Failed: " << GetLastError();
+ return Err(LaunchError("CreateProcess", GetLastError()));
+ }
+
+ gProcessLog.print("==> process %d launched child process %d (%S)\n",
+ GetCurrentProcId(), process_info.dwProcessId,
+ cmdline.c_str());
+
+ // Handles must be closed or they will leak
+ CloseHandle(process_info.hThread);
+
+ if (options.wait) WaitForSingleObject(process_info.hProcess, INFINITE);
+
+ // If the caller wants the process handle, we won't close it.
+ if (process_handle) {
+ *process_handle = process_info.hProcess;
+ } else {
+ CloseHandle(process_info.hProcess);
+ }
+ return Ok();
+}
+
+Result<Ok, LaunchError> LaunchApp(const CommandLine& cl,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ return LaunchApp(cl.command_line_string(), options, process_handle);
+}
+
+bool KillProcess(ProcessHandle process, int exit_code) {
+ // INVALID_HANDLE_VALUE is not actually an invalid handle value, but
+ // instead is the value returned by GetCurrentProcess(). nullptr,
+ // in contrast, *is* an invalid handle value. Both values are too
+ // easy to accidentally try to kill, and neither is legitimately
+ // used by this function's callers, so reject them.
+ if (!process || process == INVALID_HANDLE_VALUE) {
+ CHROMIUM_LOG(WARNING)
+ << "base::KillProcess refusing to terminate process handle " << process;
+ return false;
+ }
+ bool result = (TerminateProcess(process, exit_code) != FALSE);
+ if (!result) {
+ DLOG(ERROR) << "Unable to terminate process: " << GetLastError();
+ }
+ return result;
+}
+
+} // namespace base
+
+namespace mozilla {
+
+EnvironmentLog::EnvironmentLog(const char* varname, size_t len) {
+ wchar_t wvarname[len];
+ std::copy(varname, varname + len, wvarname);
+ const wchar_t* e = _wgetenv(wvarname);
+ if (e && *e) {
+ fname_ = e;
+ }
+}
+
+void EnvironmentLog::print(const char* format, ...) {
+ if (!fname_.size()) return;
+
+ FILE* f;
+ if (fname_.compare(L"-") == 0) {
+ f = fdopen(dup(STDOUT_FILENO), "a");
+ } else {
+ f = _wfopen(fname_.c_str(), L"a");
+ }
+
+ if (!f) return;
+
+ va_list a;
+ va_start(a, format);
+ vfprintf(f, format, a);
+ va_end(a);
+ fclose(f);
+}
+
+} // namespace mozilla