Separate process waiting from process-reference release to eliminate Windows HANDLE reuse race
Some checks are pending
Build / build-mac (push) Waiting to run
Build / build-linux (push) Waiting to run
Build / build-arm32 (push) Waiting to run
Build / build-win (push) Waiting to run
Build / build-freebsd (push) Waiting to run
Build / build-musl (push) Waiting to run
CodeQL Advanced / Analyze (c-cpp) (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run

This commit is contained in:
Andrea Mazzoleni 2026-09-04 13:16:55 +02:00
parent 5381d0a1ee
commit 11fee73ec4
4 changed files with 36 additions and 12 deletions

View file

@ -337,7 +337,9 @@ static int windows_ps(const char* ps_command, char* out, size_t out_size)
close(stdout_fd);
status = 0;
if (os_wait(pid, &status) < 0) {
pid_t pid_ret = os_wait(pid, &status);
os_dispose(pid);
if (pid_ret < 0) {
log_error(errno, "Failed to wait for PowerShell command '%s' (from os_wait).\n", ps_command);
return -1;
}

View file

@ -3165,14 +3165,18 @@ pid_t os_wait(pid_t pid, int* status)
WaitForSingleObject(h, INFINITE);
if (GetExitCodeProcess(h, &exit_code)) {
CloseHandle(h);
*status = exit_code;
return pid;
} else {
windows_errno(GetLastError());
return -1;
}
}
CloseHandle(h);
return -1;
void os_dispose(pid_t pid)
{
if (pid > 0)
CloseHandle((HANDLE)(intptr_t)pid);
}
/**

21
os/os.h
View file

@ -127,24 +127,33 @@ pid_t os_spawn(char** argv, int* stdout_read_fd, int* stderr_read_fd, const char
/**
* Wait for the child process to terminate.
* \param pid Process ID of the child process.
* This function does not release the process reference returned by os_spawn().
* The caller must eventually call os_dispose().
* \param pid Process reference returned by os_spawn().
* \param status Pointer to store the exit status.
* \return Child PID on success, -1 on failure.
* \return Child process reference on success, -1 on failure.
*/
pid_t os_wait(pid_t pid, int* status);
/**
* Release the process reference returned by os_spawn().
* On Unix this is a no-op. On Windows this closes the process HANDLE.
* \param pid Process reference returned by os_spawn().
*/
void os_dispose(pid_t pid);
/**
* Terminate gracefully a process. Intended for daemon/service processes controlling child processes.
* The caller must subsequently call os_wait() to reap process resources and close handles.
* \param pid Process ID of the process to terminate.
* The caller retains ownership and must eventually call both os_wait() and os_dispose() as appropriate.
* \param pid Process reference of the process to terminate.
* \return 0 on success, -1 on failure.
*/
int os_term(pid_t pid);
/**
* Forcibly terminate a process.
* The caller must subsequently call os_wait() to reap process resources and close handles.
* \param pid Process ID of the process to kill.
* The caller retains ownership and must eventually call both os_wait() and os_dispose() as appropriate.
* \param pid Process reference of the process to kill.
* \return 0 on success, -1 on failure.
*/
int os_kill(pid_t pid);

View file

@ -1427,6 +1427,11 @@ pid_t os_wait(pid_t pid, int* status)
return ret;
}
void os_dispose(pid_t pid)
{
(void)pid;
}
int os_term(pid_t pid)
{
if (pid <= 0) {
@ -1484,6 +1489,7 @@ int os_spawn_and_wait(const char** argv)
int status;
int ret = os_wait(pid, &status);
os_dispose(pid);
if (ret == -1) {
/* LCOV_EXCL_START */
return -1;
@ -1519,6 +1525,7 @@ OS_FILE* os_popen(const char** argv)
close(stdout_fd);
int status;
os_wait(pid, &status);
os_dispose(pid);
errno = saved_errno;
free(os_file);
return 0;
@ -1547,7 +1554,9 @@ int os_pclose(OS_FILE* stream)
fclose(fp);
int status = 0;
if (os_wait(pid, &status) < 0) {
int ret = os_wait(pid, &status);
os_dispose(pid);
if (ret < 0) {
return -1;
}