From 94cf0b522bf316c049824016bea74758752f9320 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Sat, 1 Aug 2026 18:26:01 +0200 Subject: [PATCH] Configure PR_SET_PDEATHSIG on Linux and Job Objects on Windows to prevent orphaned subprocesses on program crash --- configure.ac | 9 +++------ os/mingw.c | 22 ++++++++++++++++++++++ os/portable.h | 5 ++++- os/unix.c | 24 ++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 9fea8247..88459839 100644 --- a/configure.ac +++ b/configure.ac @@ -70,12 +70,9 @@ dnl Checks for header files. AC_HEADER_ASSERT AC_HEADER_DIRENT AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS([fcntl.h stddef.h stdint.h stdlib.h string.h limits.h time.h sys/time.h]) -AC_CHECK_HEADERS([unistd.h getopt.h io.h inttypes.h byteswap.h]) -AC_CHECK_HEADERS([pthread.h math.h syslog.h grp.h pwd.h]) -AC_CHECK_HEADERS([sys/file.h sys/ioctl.h sys/sysmacros.h sys/mkdev.h sys/mount.h]) -AC_CHECK_HEADERS([linux/fiemap.h linux/fs.h linux/btrfs.h execinfo.h]) -AC_CHECK_HEADERS([mach/mach_time.h sys/sysctl.h]) +AC_CHECK_HEADERS([stdint.h inttypes.h unistd.h math.h execinfo.h strings.h getopt.h syslog.h grp.h pwd.h io.h]) +AC_CHECK_HEADERS([sys/file.h sys/sysctl.h sys/ioctl.h sys/time.h sys/types.h sys/mkdev.h sys/sysmacros.h sys/stat.h sys/prctl.h sys/sysinfo.h sys/utsname.h]) +AC_CHECK_HEADERS([linux/fs.h linux/btrfs.h linux/fiemap.h mach/mach_time.h]) # Check for the close_range(...,CLOSE_RANGE_CLOEXEC) AC_CHECK_HEADERS([linux/close_range.h]) diff --git a/os/mingw.c b/os/mingw.c index 01ed8722..0e86120d 100644 --- a/os/mingw.c +++ b/os/mingw.c @@ -2531,6 +2531,8 @@ static int argcat(WCHAR* cmd, int size, int pos, const WCHAR* arg) return pos; } +static HANDLE os_job_handle = NULL; + pid_t os_spawn(char** argv, int* stdout_read_int, int* stderr_read_int, const char* run_as_user) { wchar_t conv[CONV_MAX]; @@ -2743,6 +2745,14 @@ pid_t os_spawn(char** argv, int* stdout_read_int, int* stderr_read_int, const ch return -1; } + /* + * Assign spawned process to Job Object configured with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE. + * This ensures Windows automatically terminates child processes if the daemon crashes unexpectedly. + */ + if (os_job_handle != NULL) { + AssignProcessToJobObject(os_job_handle, pi.hProcess); + } + /* close the write end of the pipes in the parent */ if (has_out) { CloseHandle(stdout_write_handle); @@ -3526,6 +3536,18 @@ void os_init(unsigned opt) os_exit(); } + /* initialize job object to terminate child processes if daemon dies */ + os_job_handle = CreateJobObjectW(NULL, NULL); + if (os_job_handle != NULL) { + JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; + ZeroMemory(&info, sizeof(info)); + info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + if (!SetInformationJobObject(os_job_handle, JobObjectExtendedLimitInformation, &info, sizeof(info))) { + CloseHandle(os_job_handle); + os_job_handle = NULL; + } + } + /* check for Wine presence */ is_wine = GetProcAddress(ntdll, "wine_get_version") != 0; diff --git a/os/portable.h b/os/portable.h index 4dc0460e..bd96b64e 100644 --- a/os/portable.h +++ b/os/portable.h @@ -217,6 +217,10 @@ #include #endif +#if HAVE_SYS_PRCTL_H +#include +#endif + #if HAVE_GETOPT_H #include #endif @@ -309,4 +313,3 @@ #endif #endif - diff --git a/os/unix.c b/os/unix.c index 4522e4b1..8e2c27a9 100644 --- a/os/unix.c +++ b/os/unix.c @@ -783,6 +783,14 @@ int os_script(char** argv, char** envp, const char* run_as_user) */ setpgid(0, 0); +#if defined(__linux__) && defined(PR_SET_PDEATHSIG) + /* Send SIGKILL to child if parent daemon dies unexpectedly */ + prctl(PR_SET_PDEATHSIG, SIGKILL); + if (getppid() == 1) { + _exit(127); + } +#endif + /* drop privileges first (if configured) */ if (run_as_user && run_as_user[0] != 0) { errno = 0; @@ -954,6 +962,14 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t */ setpgid(0, 0); +#if defined(__linux__) && defined(PR_SET_PDEATHSIG) + /* Send SIGKILL to child if parent daemon dies unexpectedly */ + prctl(PR_SET_PDEATHSIG, SIGKILL); + if (getppid() == 1) { + _exit(127); + } +#endif + if (pipe_fds[1] != -1) close(pipe_fds[1]); /* Close unused write end */ @@ -1177,6 +1193,14 @@ pid_t os_spawn(char** argv, int* stdout_read_fd, int* stderr_read_fd, const char setpgid(0, 0); +#if defined(__linux__) && defined(PR_SET_PDEATHSIG) + /* Send SIGKILL to child if parent daemon dies unexpectedly */ + prctl(PR_SET_PDEATHSIG, SIGKILL); + if (getppid() == 1) { + _exit(127); + } +#endif + /* drop privileges first (if configured) */ if (run_as_user && run_as_user[0] != 0) { errno = 0;