Configure PR_SET_PDEATHSIG on Linux and Job Objects on Windows to prevent orphaned subprocesses on program crash
Some checks are pending
Build / build-mac (push) Waiting to run
Build / build-linux (push) Waiting to run
Build / build-win (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-08-01 18:26:01 +02:00
parent 9a1987e024
commit 1f9ba62800
4 changed files with 73 additions and 7 deletions

View file

@ -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])

View file

@ -2531,6 +2531,8 @@ static int argcat(WCHAR* cmd, int size, int pos, const WCHAR* arg)
return pos;
}
static HANDLE os_job_handle = 0;
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 != 0) {
AssignProcessToJobObject(os_job_handle, pi.hProcess);
}
/* close the write end of the pipes in the parent */
if (has_out) {
CloseHandle(stdout_write_handle);
@ -3531,6 +3541,18 @@ void os_init(unsigned opt)
os_exit();
}
/* initialize job object to terminate child processes if daemon dies */
os_job_handle = CreateJobObjectW(0, 0);
if (os_job_handle != 0) {
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 = 0;
}
}
/* check for Wine presence */
is_wine = GetProcAddress(ntdll, "wine_get_version") != 0;
@ -3553,6 +3575,11 @@ void os_init(unsigned opt)
void os_done(void)
{
if (os_job_handle != 0) {
CloseHandle(os_job_handle);
os_job_handle = 0;
}
/* delete the thread local storage for strerror() */
windows_key_delete(last_error);

View file

@ -217,6 +217,10 @@
#include <sys/wait.h>
#endif
#if HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
#if HAVE_GETOPT_H
#include <getopt.h>
#endif
@ -309,4 +313,3 @@
#endif
#endif

View file

@ -765,6 +765,10 @@ int os_script(char** argv, char** envp, const char* run_as_user)
start = os_tick_sec();
#if defined(__linux__) && defined(PR_SET_PDEATHSIG)
pid_t ppid = getpid();
#endif
pid = fork();
if (pid < 0) {
os_syslog(OS_LVL_INFO, "failed to fork script=%s, errno=%s(%d)", resolved_path, strerror(errno), errno);
@ -783,6 +787,15 @@ 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);
/* exit if parent died before prctl to avoid becoming orphaned */
if (getppid() != ppid) {
_exit(127);
}
#endif
/* drop privileges first (if configured) */
if (run_as_user && run_as_user[0] != 0) {
errno = 0;
@ -933,6 +946,10 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t
start = os_tick_sec();
#if defined(__linux__) && defined(PR_SET_PDEATHSIG)
pid_t ppid = getpid();
#endif
pid = fork();
if (pid < 0) {
os_syslog(OS_LVL_INFO, "failed to fork command=%s, errno=%s(%d)", command, strerror(errno), errno);
@ -954,6 +971,15 @@ 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);
/* exit if parent died before prctl to avoid becoming orphaned */
if (getppid() != ppid) {
_exit(127);
}
#endif
if (pipe_fds[1] != -1)
close(pipe_fds[1]); /* Close unused write end */
@ -1157,6 +1183,10 @@ pid_t os_spawn(char** argv, int* stdout_read_fd, int* stderr_read_fd, const char
}
}
#if defined(__linux__) && defined(PR_SET_PDEATHSIG)
pid_t ppid = getpid();
#endif
pid = fork();
if (pid < 0) {
os_syslog(OS_LVL_INFO, "failed to fork path=%s, errno=%s(%d)", resolved_path, strerror(errno), errno);
@ -1177,6 +1207,15 @@ 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);
/* exit if parent died before prctl to avoid becoming orphaned */
if (getppid() != ppid) {
_exit(127);
}
#endif
/* drop privileges first (if configured) */
if (run_as_user && run_as_user[0] != 0) {
errno = 0;