Files
sdk/runtime/bin/create_process_test_helper.cc
T
Slava Egorov 473149f0b1 [io] Fix Process::Exec stdio handle inheritance
The code for inheriting stdio handles was not taking into account that
some of the stdio handles might be the same causing them to be
duplicated in the list of inherited handles. However passing duplicate
handles in PROC_THREAD_ATTRIBUTE_HANDLE_LIST causes CreateProcess to
return E_INVALIDARG.

This CL fixes the code to avoid duplicate handles.

We also fix printing of errors, which was broken when Process::Exec
was used without Dart scope causing it to print (null) instead of
actual error message.

Fixes https://github.com/dart-lang/sdk/issues/61981

TEST=vm/dart/regress_61981

Fixed: 61981
Change-Id: I93a6b56476f5020de9ebf894614cee51586b6751
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/464382
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
2025-11-25 11:59:21 -08:00

139 lines
4.5 KiB
C++

// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This is a utility program for testing that Dart binary correctly handles
// situations when stdout and stderr handles are the same.
//
// This can happen in certain terminal emulators, e.g. one used by GitBash
// see https://github.com/dart-lang/sdk/issues/61981 for an example.
#include "platform/globals.h"
#if defined(DART_HOST_OS_WINDOWS)
#include <cstdio>
#include <source_location>
#include <sstream>
#include <vector>
struct StdioHandles {
HANDLE in;
HANDLE out;
HANDLE err;
};
static void ReportErrorAndAbort(
std::source_location location = std::source_location::current()) {
const auto code = GetLastError();
wchar_t buffer[512];
auto message_size =
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buffer, ARRAY_SIZE(buffer), nullptr);
if (message_size == 0) {
_snwprintf(buffer, ARRAY_SIZE(buffer), L"OS Error %d", code);
}
fprintf(stderr, "error at %s:%d: %ls", location.file_name(), location.line(),
buffer);
abort();
}
static void LaunchProcessWith(wchar_t* command_line,
const StdioHandles& stdio_handles) {
fprintf(stderr, "LAUNCHING %ls\n", command_line);
// Setup info
STARTUPINFOEXW startup_info;
ZeroMemory(&startup_info, sizeof(startup_info));
startup_info.StartupInfo.cb = sizeof(startup_info);
// Setup the handles to inherit. We only want to inherit the three
// handles for stdin, stdout and stderr.
startup_info.StartupInfo.hStdInput = stdio_handles.in;
startup_info.StartupInfo.hStdOutput = stdio_handles.out;
startup_info.StartupInfo.hStdError = stdio_handles.err;
startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
SIZE_T size = 0;
// The call to determine the size of an attribute list always fails with
// ERROR_INSUFFICIENT_BUFFER and that error should be ignored.
if (!InitializeProcThreadAttributeList(nullptr, 1, 0, &size) &&
(GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
return ReportErrorAndAbort();
}
auto attribute_list =
reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(size));
ZeroMemory(attribute_list, size);
if (!InitializeProcThreadAttributeList(attribute_list, 1, 0, &size)) {
return ReportErrorAndAbort();
}
std::vector<HANDLE> inherited_handles = {stdio_handles.in};
if (stdio_handles.out != stdio_handles.in) {
inherited_handles.push_back(stdio_handles.out);
}
if (stdio_handles.err != stdio_handles.out &&
stdio_handles.err != stdio_handles.in) {
inherited_handles.push_back(stdio_handles.err);
}
if (!UpdateProcThreadAttribute(
attribute_list, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
inherited_handles.data(), inherited_handles.size() * sizeof(HANDLE),
nullptr, nullptr)) {
return ReportErrorAndAbort();
}
startup_info.lpAttributeList = attribute_list;
PROCESS_INFORMATION process_info;
ZeroMemory(&process_info, sizeof(process_info));
// Create process.
BOOL result = CreateProcessW(
/*lpApplicationName=*/nullptr, command_line,
/*lpProcessAttributes=*/nullptr,
/*lpThreadAttributes=*/nullptr,
/*bInheritHandles=*/TRUE,
/*dwCreationFlags=*/EXTENDED_STARTUPINFO_PRESENT,
/*lpEnvironment=*/nullptr,
/*lpCurrentDirectory=*/nullptr,
reinterpret_cast<STARTUPINFOW*>(&startup_info), &process_info);
if (result == 0) {
return ReportErrorAndAbort();
}
WaitForSingleObject(process_info.hProcess, INFINITE);
CloseHandle(process_info.hProcess);
CloseHandle(process_info.hThread);
}
int main(int argc, char* argv[]) {
if (argc <= 1) {
fprintf(stderr, "Usage: %s <executable> <arg0> ... <argN>\n", argv[0]);
return -1;
}
// Generate command line. Assume that it does not contain any white-space
// in the arguments.
std::wstringstream wstr;
for (int i = 1; i < argc; i++) {
wstr << (i > 1 ? " " : "") << argv[i];
}
HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
LaunchProcessWith(
wstr.str().data(),
{.in = stdin_handle, .out = stdout_handle, .err = stdout_handle});
CloseHandle(stdin_handle);
CloseHandle(stdout_handle);
return 0;
}
#else
int main() {
return -1;
}
#endif // defined(DART_HOST_OS_WINDOWS)