From 11fee73ec43c1653c8b3f68fc73c073ffc37141d Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Fri, 4 Sep 2026 13:16:55 +0200 Subject: [PATCH] Separate process waiting from process-reference release to eliminate Windows HANDLE reuse race --- cmdline/mingwapp.c | 4 +++- os/mingw.c | 12 ++++++++---- os/os.h | 21 +++++++++++++++------ os/unix.c | 11 ++++++++++- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/cmdline/mingwapp.c b/cmdline/mingwapp.c index 2c0ff4d5..772a7589 100644 --- a/cmdline/mingwapp.c +++ b/cmdline/mingwapp.c @@ -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; } diff --git a/os/mingw.c b/os/mingw.c index b495775e..4dcfeef9 100644 --- a/os/mingw.c +++ b/os/mingw.c @@ -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); } /** diff --git a/os/os.h b/os/os.h index 2ba71c12..3bd6d211 100644 --- a/os/os.h +++ b/os/os.h @@ -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); diff --git a/os/unix.c b/os/unix.c index 72518e3c..9d5e7291 100644 --- a/os/unix.c +++ b/os/unix.c @@ -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; }