Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect death of parent process from `setuid` process

I write C application that calls fork() to create child processes. The application runs as root. In the parent process, I use wait() for waiting terminated child processes. In child processes, I use prctl() with PR_SET_PDEATHSIG option to detect the death of the parent. It works fine. To reduce the risk of security issues, child processes call setuid() to change UID. The problem is: child processes can not detect the death of the parent one any more.

I have searched around to find the answer and found some useful links, but it does not help:

  • Detect death of parent process
  • Enforcing process hierarchies (prctl related) : although this link contains a clear answer, there is no solution.

How to do that correctly?

like image 338
QuangNHb Avatar asked Sep 20 '25 22:09

QuangNHb


2 Answers

I just stumbled upon the same issue, the kernel resets the PDEATH signal on credential change:

https://github.com/torvalds/linux/blob/master/kernel/cred.c#L450

This can be verified with the following code and strace -f:

#include <sys/prctl.h>
#include <unistd.h>
#include <signal.h>

int main(int argc, char *argv[])
{
        if (fork() == 0) {
                // This works as expected
                setgid(1000);                                                                                                                                                                                       
                setuid(1000);

                prctl(PR_SET_PDEATHSIG, SIGTERM);

                // This doesn't work since pdeath_signal will be reset
                // setgid(1000);
                // setuid(1000);

                pause();
        }
        sleep(1);
        kill(getpid(), SIGTERM);
        return (0);
}
like image 91
3XX0 Avatar answered Sep 22 '25 18:09

3XX0


Just a hint for anyone still having the same issue. If the circumstances of process death aren't important (exit code and/or signal) there is work around. One can detect a process death by secondary indicators. One can rely on the *nix feature that guarantees that on exit each process closes all of its descriptors. Knowing this one can create a simple pipe, where each end is shared by each party.

Then, a simple select on one end of the pipe will detect an event of the other end being closed (process exit). It's very common in this case to already have some communication line between parent and child (stdin/stdout/strderr or a custom pipe/unix-socket). In which case this is all that is needed here.

like image 45
GreenScape Avatar answered Sep 22 '25 20:09

GreenScape