launch_win.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/process/launch.h"
  5. #include <fcntl.h>
  6. #include <io.h>
  7. // windows.h must be included before shellapi.h
  8. #include <windows.h>
  9. #include <psapi.h>
  10. #include <shellapi.h>
  11. #include <userenv.h>
  12. #include <ios>
  13. #include <limits>
  14. #include "base/bind.h"
  15. #include "base/callback_helpers.h"
  16. #include "base/debug/activity_tracker.h"
  17. #include "base/debug/stack_trace.h"
  18. #include "base/logging.h"
  19. #include "base/metrics/histogram.h"
  20. #include "base/process/environment_internal.h"
  21. #include "base/process/kill.h"
  22. #include "base/strings/string_util.h"
  23. #include "base/strings/utf_string_conversions.h"
  24. #include "base/system/sys_info.h"
  25. #include "base/threading/scoped_blocking_call.h"
  26. #include "base/threading/scoped_thread_priority.h"
  27. #include "base/trace_event/base_tracing.h"
  28. #include "base/win/scoped_handle.h"
  29. #include "base/win/scoped_process_information.h"
  30. #include "base/win/startup_information.h"
  31. #include "base/win/windows_version.h"
  32. namespace base {
  33. namespace {
  34. bool GetAppOutputInternal(CommandLine::StringPieceType cl,
  35. bool include_stderr,
  36. std::string* output,
  37. int* exit_code) {
  38. TRACE_EVENT0("base", "GetAppOutput");
  39. HANDLE out_read = nullptr;
  40. HANDLE out_write = nullptr;
  41. SECURITY_ATTRIBUTES sa_attr;
  42. // Set the bInheritHandle flag so pipe handles are inherited.
  43. sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
  44. sa_attr.bInheritHandle = TRUE;
  45. sa_attr.lpSecurityDescriptor = nullptr;
  46. // Create the pipe for the child process's STDOUT.
  47. if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
  48. NOTREACHED() << "Failed to create pipe";
  49. return false;
  50. }
  51. // Ensure we don't leak the handles.
  52. win::ScopedHandle scoped_out_read(out_read);
  53. win::ScopedHandle scoped_out_write(out_write);
  54. // Ensure the read handles to the pipes are not inherited.
  55. if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
  56. NOTREACHED() << "Failed to disabled pipe inheritance";
  57. return false;
  58. }
  59. FilePath::StringType writable_command_line_string(cl);
  60. STARTUPINFO start_info = {};
  61. start_info.cb = sizeof(STARTUPINFO);
  62. start_info.hStdOutput = out_write;
  63. // Keep the normal stdin.
  64. start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  65. if (include_stderr) {
  66. start_info.hStdError = out_write;
  67. } else {
  68. start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
  69. }
  70. start_info.dwFlags |= STARTF_USESTDHANDLES;
  71. // Create the child process.
  72. PROCESS_INFORMATION temp_process_info = {};
  73. if (!CreateProcess(nullptr, data(writable_command_line_string), nullptr,
  74. nullptr,
  75. TRUE, // Handles are inherited.
  76. 0, nullptr, nullptr, &start_info, &temp_process_info)) {
  77. NOTREACHED() << "Failed to start process";
  78. return false;
  79. }
  80. win::ScopedProcessInformation proc_info(temp_process_info);
  81. debug::GlobalActivityTracker* tracker = debug::GlobalActivityTracker::Get();
  82. if (tracker)
  83. tracker->RecordProcessLaunch(proc_info.process_id(),
  84. CommandLine::StringType(cl));
  85. // Close our writing end of pipe now. Otherwise later read would not be able
  86. // to detect end of child's output.
  87. scoped_out_write.Close();
  88. // Read output from the child process's pipe for STDOUT
  89. const int kBufferSize = 1024;
  90. char buffer[kBufferSize];
  91. for (;;) {
  92. DWORD bytes_read = 0;
  93. BOOL success =
  94. ::ReadFile(out_read, buffer, kBufferSize, &bytes_read, nullptr);
  95. if (!success || bytes_read == 0)
  96. break;
  97. output->append(buffer, bytes_read);
  98. }
  99. // Let's wait for the process to finish.
  100. {
  101. // It is okay to allow this process to wait on the launched process as a
  102. // process launched with GetAppOutput*() shouldn't wait back on the process
  103. // that launched it.
  104. internal::GetAppOutputScopedAllowBaseSyncPrimitives allow_wait;
  105. ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
  106. WaitForSingleObject(proc_info.process_handle(), INFINITE);
  107. }
  108. TerminationStatus status =
  109. GetTerminationStatus(proc_info.process_handle(), exit_code);
  110. debug::GlobalActivityTracker::RecordProcessExitIfEnabled(
  111. proc_info.process_id(), *exit_code);
  112. return status != TERMINATION_STATUS_PROCESS_CRASHED &&
  113. status != TERMINATION_STATUS_ABNORMAL_TERMINATION;
  114. }
  115. Process LaunchElevatedProcess(const CommandLine& cmdline,
  116. bool start_hidden,
  117. bool wait) {
  118. TRACE_EVENT0("base", "LaunchElevatedProcess");
  119. const FilePath::StringType file = cmdline.GetProgram().value();
  120. const CommandLine::StringType arguments = cmdline.GetArgumentsString();
  121. SHELLEXECUTEINFO shex_info = {};
  122. shex_info.cbSize = sizeof(shex_info);
  123. shex_info.fMask = SEE_MASK_NOCLOSEPROCESS;
  124. shex_info.hwnd = GetActiveWindow();
  125. shex_info.lpVerb = L"runas";
  126. shex_info.lpFile = file.c_str();
  127. shex_info.lpParameters = arguments.c_str();
  128. shex_info.lpDirectory = nullptr;
  129. shex_info.nShow = start_hidden ? SW_HIDE : SW_SHOWNORMAL;
  130. shex_info.hInstApp = nullptr;
  131. if (!ShellExecuteEx(&shex_info)) {
  132. DPLOG(ERROR);
  133. return Process();
  134. }
  135. if (wait) {
  136. ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
  137. WaitForSingleObject(shex_info.hProcess, INFINITE);
  138. }
  139. debug::GlobalActivityTracker::RecordProcessLaunchIfEnabled(
  140. GetProcessId(shex_info.hProcess), file, arguments);
  141. return Process(shex_info.hProcess);
  142. }
  143. } // namespace
  144. void RouteStdioToConsole(bool create_console_if_not_found) {
  145. // Don't change anything if stdout or stderr already point to a
  146. // valid stream.
  147. //
  148. // If we are running under Buildbot or under Cygwin's default
  149. // terminal (mintty), stderr and stderr will be pipe handles. In
  150. // that case, we don't want to open CONOUT$, because its output
  151. // likely does not go anywhere.
  152. //
  153. // We don't use GetStdHandle() to check stdout/stderr here because
  154. // it can return dangling IDs of handles that were never inherited
  155. // by this process. These IDs could have been reused by the time
  156. // this function is called. The CRT checks the validity of
  157. // stdout/stderr on startup (before the handle IDs can be reused).
  158. // _fileno(stdout) will return -2 (_NO_CONSOLE_FILENO) if stdout was
  159. // invalid.
  160. if (_fileno(stdout) >= 0 || _fileno(stderr) >= 0) {
  161. // _fileno was broken for SUBSYSTEM:WINDOWS from VS2010 to VS2012/2013.
  162. // http://crbug.com/358267. Confirm that the underlying HANDLE is valid
  163. // before aborting.
  164. intptr_t stdout_handle = _get_osfhandle(_fileno(stdout));
  165. intptr_t stderr_handle = _get_osfhandle(_fileno(stderr));
  166. if (stdout_handle >= 0 || stderr_handle >= 0)
  167. return;
  168. }
  169. if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
  170. unsigned int result = GetLastError();
  171. // Was probably already attached.
  172. if (result == ERROR_ACCESS_DENIED)
  173. return;
  174. // Don't bother creating a new console for each child process if the
  175. // parent process is invalid (eg: crashed).
  176. if (result == ERROR_GEN_FAILURE)
  177. return;
  178. if (create_console_if_not_found) {
  179. // Make a new console if attaching to parent fails with any other error.
  180. // It should be ERROR_INVALID_HANDLE at this point, which means the
  181. // browser was likely not started from a console.
  182. AllocConsole();
  183. } else {
  184. return;
  185. }
  186. }
  187. // Arbitrary byte count to use when buffering output lines. More
  188. // means potential waste, less means more risk of interleaved
  189. // log-lines in output.
  190. enum { kOutputBufferSize = 64 * 1024 };
  191. if (freopen("CONOUT$", "w", stdout)) {
  192. setvbuf(stdout, nullptr, _IOLBF, kOutputBufferSize);
  193. // Overwrite FD 1 for the benefit of any code that uses this FD
  194. // directly. This is safe because the CRT allocates FDs 0, 1 and
  195. // 2 at startup even if they don't have valid underlying Windows
  196. // handles. This means we won't be overwriting an FD created by
  197. // _open() after startup.
  198. _dup2(_fileno(stdout), 1);
  199. }
  200. if (freopen("CONOUT$", "w", stderr)) {
  201. setvbuf(stderr, nullptr, _IOLBF, kOutputBufferSize);
  202. _dup2(_fileno(stderr), 2);
  203. }
  204. // Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog.
  205. std::ios::sync_with_stdio();
  206. }
  207. Process LaunchProcess(const CommandLine& cmdline,
  208. const LaunchOptions& options) {
  209. if (options.elevated)
  210. return LaunchElevatedProcess(cmdline, options.start_hidden, options.wait);
  211. return LaunchProcess(cmdline.GetCommandLineString(), options);
  212. }
  213. Process LaunchProcess(const CommandLine::StringType& cmdline,
  214. const LaunchOptions& options) {
  215. if (options.elevated) {
  216. return LaunchElevatedProcess(base::CommandLine::FromString(cmdline),
  217. options.start_hidden, options.wait);
  218. }
  219. TRACE_EVENT0("base", "LaunchProcess");
  220. // Mitigate the issues caused by loading DLLs on a background thread
  221. // (http://crbug/973868).
  222. SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
  223. // |process_mitigations| must outlive |startup_info_wrapper|.
  224. DWORD64 process_mitigations[2]{0, 0};
  225. win::StartupInformation startup_info_wrapper;
  226. STARTUPINFO* startup_info = startup_info_wrapper.startup_info();
  227. DWORD flags = 0;
  228. // Count extended attributes before reserving space.
  229. DWORD attribute_count = 0;
  230. // Count PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY.
  231. if (options.disable_cetcompat &&
  232. base::win::GetVersion() >= base::win::Version::WIN10_20H1) {
  233. ++attribute_count;
  234. }
  235. // Count PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
  236. if (!options.handles_to_inherit.empty())
  237. ++attribute_count;
  238. // Reserve space for attributes.
  239. if (attribute_count > 0) {
  240. if (!startup_info_wrapper.InitializeProcThreadAttributeList(
  241. attribute_count)) {
  242. DPLOG(ERROR);
  243. return Process();
  244. }
  245. flags |= EXTENDED_STARTUPINFO_PRESENT;
  246. }
  247. // Set PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY.
  248. if (options.disable_cetcompat &&
  249. base::win::GetVersion() >= base::win::Version::WIN10_20H1) {
  250. DCHECK_GT(attribute_count, 0u);
  251. process_mitigations[1] |=
  252. PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_OFF;
  253. if (!startup_info_wrapper.UpdateProcThreadAttribute(
  254. PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &process_mitigations[0],
  255. sizeof(process_mitigations))) {
  256. return Process();
  257. }
  258. }
  259. // Set PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
  260. bool inherit_handles = options.inherit_mode == LaunchOptions::Inherit::kAll;
  261. if (!options.handles_to_inherit.empty()) {
  262. DCHECK_GT(attribute_count, 0u);
  263. DCHECK_EQ(options.inherit_mode, LaunchOptions::Inherit::kSpecific);
  264. if (options.handles_to_inherit.size() >
  265. std::numeric_limits<DWORD>::max() / sizeof(HANDLE)) {
  266. DLOG(ERROR) << "Too many handles to inherit.";
  267. return Process();
  268. }
  269. // Ensure the handles can be inherited.
  270. for (HANDLE handle : options.handles_to_inherit) {
  271. BOOL result = SetHandleInformation(handle, HANDLE_FLAG_INHERIT,
  272. HANDLE_FLAG_INHERIT);
  273. PCHECK(result);
  274. }
  275. if (!startup_info_wrapper.UpdateProcThreadAttribute(
  276. PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
  277. const_cast<HANDLE*>(&options.handles_to_inherit[0]),
  278. static_cast<DWORD>(options.handles_to_inherit.size() *
  279. sizeof(HANDLE)))) {
  280. DPLOG(ERROR);
  281. return Process();
  282. }
  283. inherit_handles = true;
  284. }
  285. if (options.feedback_cursor_off)
  286. startup_info->dwFlags |= STARTF_FORCEOFFFEEDBACK;
  287. if (options.empty_desktop_name)
  288. startup_info->lpDesktop = const_cast<wchar_t*>(L"");
  289. startup_info->dwFlags |= STARTF_USESHOWWINDOW;
  290. startup_info->wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOWNORMAL;
  291. if (options.stdin_handle || options.stdout_handle || options.stderr_handle) {
  292. DCHECK(inherit_handles);
  293. DCHECK(options.stdin_handle);
  294. DCHECK(options.stdout_handle);
  295. DCHECK(options.stderr_handle);
  296. startup_info->dwFlags |= STARTF_USESTDHANDLES;
  297. startup_info->hStdInput = options.stdin_handle;
  298. startup_info->hStdOutput = options.stdout_handle;
  299. startup_info->hStdError = options.stderr_handle;
  300. }
  301. if (options.job_handle) {
  302. // If this code is run under a debugger, the launched process is
  303. // automatically associated with a job object created by the debugger.
  304. // The CREATE_BREAKAWAY_FROM_JOB flag is used to prevent this on Windows
  305. // releases that do not support nested jobs.
  306. if (win::GetVersion() < win::Version::WIN8)
  307. flags |= CREATE_BREAKAWAY_FROM_JOB;
  308. }
  309. if (options.force_breakaway_from_job_)
  310. flags |= CREATE_BREAKAWAY_FROM_JOB;
  311. PROCESS_INFORMATION temp_process_info = {};
  312. LPCTSTR current_directory = options.current_directory.empty()
  313. ? nullptr
  314. : options.current_directory.value().c_str();
  315. auto writable_cmdline(cmdline);
  316. DCHECK(!(flags & CREATE_SUSPENDED))
  317. << "Creating a suspended process can lead to hung processes if the "
  318. << "launching process is killed before it assigns the process to the"
  319. << "job. https://crbug.com/820996";
  320. if (options.as_user) {
  321. flags |= CREATE_UNICODE_ENVIRONMENT;
  322. void* environment_block = nullptr;
  323. if (!CreateEnvironmentBlock(&environment_block, options.as_user, FALSE)) {
  324. DPLOG(ERROR);
  325. return Process();
  326. }
  327. // Environment options are not implemented for use with |as_user|.
  328. DCHECK(!options.clear_environment);
  329. DCHECK(options.environment.empty());
  330. BOOL launched = CreateProcessAsUser(
  331. options.as_user, nullptr, data(writable_cmdline), nullptr, nullptr,
  332. inherit_handles, flags, environment_block, current_directory,
  333. startup_info, &temp_process_info);
  334. DestroyEnvironmentBlock(environment_block);
  335. if (!launched) {
  336. DPLOG(ERROR) << "Command line:" << std::endl
  337. << WideToUTF8(cmdline) << std::endl;
  338. return Process();
  339. }
  340. } else {
  341. wchar_t* new_environment = nullptr;
  342. std::wstring env_storage;
  343. if (options.clear_environment || !options.environment.empty()) {
  344. if (options.clear_environment) {
  345. static const wchar_t kEmptyEnvironment[] = {0};
  346. env_storage =
  347. internal::AlterEnvironment(kEmptyEnvironment, options.environment);
  348. } else {
  349. wchar_t* old_environment = GetEnvironmentStrings();
  350. if (!old_environment) {
  351. DPLOG(ERROR);
  352. return Process();
  353. }
  354. env_storage =
  355. internal::AlterEnvironment(old_environment, options.environment);
  356. FreeEnvironmentStrings(old_environment);
  357. }
  358. new_environment = data(env_storage);
  359. flags |= CREATE_UNICODE_ENVIRONMENT;
  360. }
  361. if (!CreateProcess(nullptr, data(writable_cmdline), nullptr, nullptr,
  362. inherit_handles, flags, new_environment,
  363. current_directory, startup_info, &temp_process_info)) {
  364. DPLOG(ERROR) << "Command line:" << std::endl << cmdline << std::endl;
  365. return Process();
  366. }
  367. }
  368. win::ScopedProcessInformation process_info(temp_process_info);
  369. if (options.job_handle &&
  370. !AssignProcessToJobObject(options.job_handle,
  371. process_info.process_handle())) {
  372. DPLOG(ERROR) << "Could not AssignProcessToObject";
  373. Process scoped_process(process_info.TakeProcessHandle());
  374. scoped_process.Terminate(win::kProcessKilledExitCode, true);
  375. return Process();
  376. }
  377. if (options.grant_foreground_privilege &&
  378. !AllowSetForegroundWindow(GetProcId(process_info.process_handle()))) {
  379. DPLOG(ERROR) << "Failed to grant foreground privilege to launched process";
  380. }
  381. if (options.wait) {
  382. ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
  383. WaitForSingleObject(process_info.process_handle(), INFINITE);
  384. }
  385. debug::GlobalActivityTracker::RecordProcessLaunchIfEnabled(
  386. process_info.process_id(), cmdline);
  387. return Process(process_info.TakeProcessHandle());
  388. }
  389. bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) {
  390. JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {};
  391. limit_info.BasicLimitInformation.LimitFlags = limit_flags;
  392. return 0 != SetInformationJobObject(
  393. job_object,
  394. JobObjectExtendedLimitInformation,
  395. &limit_info,
  396. sizeof(limit_info));
  397. }
  398. bool GetAppOutput(const CommandLine& cl, std::string* output) {
  399. return GetAppOutput(cl.GetCommandLineString(), output);
  400. }
  401. bool GetAppOutputAndError(const CommandLine& cl, std::string* output) {
  402. int exit_code;
  403. return GetAppOutputInternal(
  404. cl.GetCommandLineString(), true, output, &exit_code);
  405. }
  406. bool GetAppOutputWithExitCode(const CommandLine& cl,
  407. std::string* output,
  408. int* exit_code) {
  409. return GetAppOutputInternal(
  410. cl.GetCommandLineString(), false, output, exit_code);
  411. }
  412. bool GetAppOutput(CommandLine::StringPieceType cl, std::string* output) {
  413. int exit_code;
  414. return GetAppOutputInternal(cl, false, output, &exit_code);
  415. }
  416. void RaiseProcessToHighPriority() {
  417. SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
  418. }
  419. } // namespace base