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
This commit is contained in:
Jameson Nash 2023-10-02 15:15:18 +02:00 committed by GitHub
parent 2a4cab70ef
commit c03569f0df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -105,6 +105,21 @@ static void uv__init_global_job_handle(void) {
&info, &info,
sizeof info)) sizeof info))
uv_fatal_error(GetLastError(), "SetInformationJobObject"); 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. * breakaway.
*/ */
process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
process_flags |= CREATE_SUSPENDED;
} }
if (!CreateProcessW(application_path, if (!CreateProcessW(application_path,
@ -1116,11 +1132,6 @@ int uv_spawn(uv_loop_t* loop,
goto done; 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 /* If the process isn't spawned as detached, assign to the global job object
* so windows will kill it when the parent process dies. */ * so windows will kill it when the parent process dies. */
if (!(options->flags & UV_PROCESS_DETACHED)) { 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. */ /* Set IPC pid to all IPC pipes. */
for (i = 0; i < options->stdio_count; i++) { for (i = 0; i < options->stdio_count; i++) {
const uv_stdio_container_t* fdopt = &options->stdio[i]; const uv_stdio_container_t* fdopt = &options->stdio[i];