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