This commit is contained in:
Jameson Nash 2026-03-31 09:18:44 +03:00 committed by GitHub
commit 86e9ab79ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 85 additions and 37 deletions

View File

@ -902,14 +902,20 @@ int uv_spawn(uv_loop_t* loop,
BOOL result;
WCHAR* application_path = NULL, *application = NULL, *arguments = NULL,
*env = NULL, *cwd = NULL;
STARTUPINFOW startup;
STARTUPINFOEXW startup;
PROCESS_INFORMATION info;
DWORD process_flags, cwd_len;
BYTE* child_stdio_buffer;
HANDLE* handle_list;
LPPROC_THREAD_ATTRIBUTE_LIST attr_list;
int attr_list_initialized;
uv__process_init(loop, process);
process->exit_cb = options->exit_cb;
child_stdio_buffer = NULL;
handle_list = NULL;
attr_list = NULL;
attr_list_initialized = 0;
if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
return UV_ENOTSUP;
@ -1022,20 +1028,70 @@ int uv_spawn(uv_loop_t* loop,
goto done;
}
startup.cb = sizeof(startup);
startup.lpReserved = NULL;
startup.lpDesktop = NULL;
startup.lpTitle = NULL;
startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
memset(&startup, 0, sizeof startup);
startup.StartupInfo.cb = sizeof(startup);
startup.StartupInfo.lpReserved = NULL;
startup.StartupInfo.lpDesktop = NULL;
startup.StartupInfo.lpTitle = NULL;
startup.StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
startup.cbReserved2 = uv__stdio_size(child_stdio_buffer);
startup.lpReserved2 = (BYTE*) child_stdio_buffer;
startup.StartupInfo.cbReserved2 = uv__stdio_size(child_stdio_buffer);
startup.StartupInfo.lpReserved2 = (BYTE*) child_stdio_buffer;
startup.hStdInput = uv__stdio_handle(child_stdio_buffer, 0);
startup.hStdOutput = uv__stdio_handle(child_stdio_buffer, 1);
startup.hStdError = uv__stdio_handle(child_stdio_buffer, 2);
startup.StartupInfo.hStdInput = uv__stdio_handle(child_stdio_buffer, 0);
startup.StartupInfo.hStdOutput = uv__stdio_handle(child_stdio_buffer, 1);
startup.StartupInfo.hStdError = uv__stdio_handle(child_stdio_buffer, 2);
process_flags = CREATE_UNICODE_ENVIRONMENT;
/* Build the list of handles to inherit. Using
* PROC_THREAD_ATTRIBUTE_HANDLE_LIST ensures only these specific handles are
* inherited, closing the race condition where concurrent uv_spawn calls
* could cause handles intended for one child to leak into another. */
{
#define CHILD_STDIO_COUNT(buffer) \
*((unsigned int*) (buffer))
int count = CHILD_STDIO_COUNT(child_stdio_buffer);
#undef CHILD_STDIO_COUNT
int n = 0;
SIZE_T attr_size = 0;
handle_list = (HANDLE*) uv__malloc(count * sizeof(HANDLE));
if (handle_list == NULL) {
err = ERROR_OUTOFMEMORY;
goto done;
}
for (i = 0; i < count; i++) {
HANDLE h = uv__stdio_handle(child_stdio_buffer, i);
if (h != INVALID_HANDLE_VALUE)
handle_list[n++] = h;
}
InitializeProcThreadAttributeList(NULL, 1, 0, &attr_size);
attr_list = (LPPROC_THREAD_ATTRIBUTE_LIST) uv__malloc(attr_size);
if (attr_list == NULL) {
err = ERROR_OUTOFMEMORY;
goto done;
}
if (!InitializeProcThreadAttributeList(attr_list, 1, 0, &attr_size)) {
err = GetLastError();
goto done;
}
attr_list_initialized = 1;
if (!UpdateProcThreadAttribute(attr_list,
0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
handle_list,
n * sizeof(HANDLE),
NULL,
NULL)) {
err = GetLastError();
goto done;
}
}
startup.lpAttributeList = attr_list;
process_flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT;
if ((options->flags & UV_PROCESS_WINDOWS_HIDE_CONSOLE) ||
(options->flags & UV_PROCESS_WINDOWS_HIDE)) {
@ -1050,9 +1106,9 @@ int uv_spawn(uv_loop_t* loop,
if ((options->flags & UV_PROCESS_WINDOWS_HIDE_GUI) ||
(options->flags & UV_PROCESS_WINDOWS_HIDE)) {
/* Use SW_HIDE to avoid any potential process window. */
startup.wShowWindow = SW_HIDE;
startup.StartupInfo.wShowWindow = SW_HIDE;
} else {
startup.wShowWindow = SW_SHOWDEFAULT;
startup.StartupInfo.wShowWindow = SW_SHOWDEFAULT;
}
if (options->flags & UV_PROCESS_DETACHED) {
@ -1078,7 +1134,7 @@ int uv_spawn(uv_loop_t* loop,
process_flags,
env,
cwd,
&startup,
&startup.StartupInfo,
&info)) {
/* CreateProcessW failed. */
err = GetLastError();
@ -1161,6 +1217,14 @@ int uv_spawn(uv_loop_t* loop,
uv__free(env);
uv__free(alloc_path);
if (attr_list != NULL) {
if (attr_list_initialized)
DeleteProcThreadAttributeList(attr_list);
uv__free(attr_list);
attr_list = NULL;
}
uv__free(handle_list);
if (child_stdio_buffer != NULL) {
/* Clean up child stdio handles. */
uv__stdio_destroy(child_stdio_buffer);

View File

@ -211,25 +211,18 @@ static int maybe_run_test(int argc, char **argv) {
if (strcmp(argv[1], "spawn_helper8") == 0) {
uv_os_fd_t closed_fd;
uv_os_fd_t open_fd;
#ifdef _WIN32
DWORD flags;
HMODULE kernelbase_module;
union {
FARPROC proc;
sCompareObjectHandles pCompareObjectHandles; /* Windows >= 10 */
} u;
#endif
notify_parent_process();
ASSERT_EQ(sizeof(closed_fd), read(0, &closed_fd, sizeof(closed_fd)));
ASSERT_EQ(sizeof(open_fd), read(0, &open_fd, sizeof(open_fd)));
#ifdef _WIN32
DWORD flags;
ASSERT_GT((intptr_t) closed_fd, 0);
ASSERT_GT((intptr_t) open_fd, 0);
ASSERT_NE(0, GetHandleInformation(open_fd, &flags));
kernelbase_module = GetModuleHandleW(L"kernelbase.dll");
u.proc = GetProcAddress(kernelbase_module, "CompareObjectHandles");
if (u.pCompareObjectHandles != NULL)
ASSERT_EQ(FALSE, u.pCompareObjectHandles(open_fd, closed_fd));
ASSERT_EQ(0, GetHandleInformation(open_fd, &flags));
ASSERT_EQ(ERROR_INVALID_HANDLE, GetLastError());
SetLastError(ERROR_SUCCESS);
ASSERT_EQ(0, GetHandleInformation(closed_fd, &flags));
ASSERT_EQ(ERROR_INVALID_HANDLE, GetLastError());
#else
ASSERT_GT(open_fd, 2);
ASSERT_GT(closed_fd, 2);

View File

@ -1677,11 +1677,6 @@ TEST_IMPL(spawn_fs_open) {
uv_stdio_container_t stdio[1];
#ifdef _WIN32
const char dev_null[] = "NUL";
HMODULE kernelbase_module;
union {
FARPROC proc;
sCompareObjectHandles pCompareObjectHandles; /* Windows >= 10 */
} u;
#else
const char dev_null[] = "/dev/null";
#endif
@ -1704,10 +1699,6 @@ TEST_IMPL(spawn_fs_open) {
#ifdef _WIN32
ASSERT_NE(0, DuplicateHandle(GetCurrentProcess(), fd, GetCurrentProcess(), &dup_fd,
0, /* inherit */ TRUE, DUPLICATE_SAME_ACCESS));
kernelbase_module = GetModuleHandleW(L"kernelbase.dll");
u.proc = GetProcAddress(kernelbase_module, "CompareObjectHandles");
if (u.pCompareObjectHandles != NULL)
ASSERT_EQ(TRUE, u.pCompareObjectHandles(fd, dup_fd));
#else
dup_fd = dup(fd);
#endif