From c03569f0df3bb26e7e7bec9feabd9ae3c372568e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 2 Oct 2023 15:15:18 +0200 Subject: [PATCH] win,process: avoid assert after spawning Store app (#4152) Make sure this handle is functional. The Windows kernel seems to have a bug that if the first use of AssignProcessToJobObject is for a Windows Store program, subsequent attempts to use the handle with fail with INVALID_PARAMETER (87). This is possilby because all uses of the handle must be for the same Terminal Services session. We can ensure it is tied to our current session now by adding ourself to it. We could remove ourself afterwards, but there doesn't seem to be a reason to. Secondly, we start the process suspended so that we can make sure we added it to the job control object before it does anything itself (such as launch more jobs or exit). Fixes: https://github.com/JuliaLang/julia/issues/51461 --- src/win/process.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/win/process.c b/src/win/process.c index 3cfa89e7..8f1b665a 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -105,6 +105,21 @@ static void uv__init_global_job_handle(void) { &info, sizeof info)) uv_fatal_error(GetLastError(), "SetInformationJobObject"); + + + if (!AssignProcessToJobObject(uv_global_job_handle_, GetCurrentProcess())) { + /* Make sure this handle is functional. The Windows kernel has a bug that + * if the first use of AssignProcessToJobObject is for a Windows Store + * program, subsequent attempts to use the handle with fail with + * INVALID_PARAMETER (87). This is possibly because all uses of the handle + * must be for the same Terminal Services session. We can ensure it is tied + * to our current session now by adding ourself to it. We could remove + * ourself afterwards, but there doesn't seem to be a reason to. + */ + DWORD err = GetLastError(); + if (err != ERROR_ACCESS_DENIED) + uv_fatal_error(err, "AssignProcessToJobObject"); + } } @@ -1099,6 +1114,7 @@ int uv_spawn(uv_loop_t* loop, * breakaway. */ process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; + process_flags |= CREATE_SUSPENDED; } if (!CreateProcessW(application_path, @@ -1116,11 +1132,6 @@ int uv_spawn(uv_loop_t* loop, goto done; } - /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */ - - process->process_handle = info.hProcess; - process->pid = info.dwProcessId; - /* If the process isn't spawned as detached, assign to the global job object * so windows will kill it when the parent process dies. */ if (!(options->flags & UV_PROCESS_DETACHED)) { @@ -1143,6 +1154,19 @@ int uv_spawn(uv_loop_t* loop, } } + if (process_flags & CREATE_SUSPENDED) { + if (ResumeThread(info.hThread) == ((DWORD)-1)) { + err = GetLastError(); + TerminateProcess(info.hProcess, 1); + goto done; + } + } + + /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */ + + process->process_handle = info.hProcess; + process->pid = info.dwProcessId; + /* Set IPC pid to all IPC pipes. */ for (i = 0; i < options->stdio_count; i++) { const uv_stdio_container_t* fdopt = &options->stdio[i];