The way we've used PDEATHSIG so far was to call it in the child immediately after fork(). This is however inherently racy, because it is absolutely possible for the parent to get SIGKILL'ed after the fork, but before the child gets to call prctl.
This fixes the race by introducing a new function, sig_setpdeathsig.
The function takes the kill signal to pass to prctl PR_SET_PDEATHSIG, as well as the expected parent pid. The way to use this function is as follows:
pid_t ppid = getpid();
pid_t pid = fork();
if (pid == -1) {
err(1, "fork");
}
if (!pid) {
// in the child
sig_setpdeathsig(SIGKILL, ppid);
}
sig_setpdeathsig then takes care of calling prctl(PR_SET_PDEATHSIG, signo), and checks whether the current ppid is the same as the original ppid. If it isn't, it means the parent died and the child got reparented to the nearest subreaper, and the function calls raise(signo).
The way we've used PDEATHSIG so far was to call it in the child immediately after fork(). This is however inherently racy, because it is absolutely possible for the parent to get SIGKILL'ed after the fork, but before the child gets to call prctl.
This fixes the race by introducing a new function, sig_setpdeathsig.
The function takes the kill signal to pass to prctl PR_SET_PDEATHSIG, as well as the expected parent pid. The way to use this function is as follows:
sig_setpdeathsig then takes care of calling prctl(PR_SET_PDEATHSIG, signo), and checks whether the current ppid is the same as the original ppid. If it isn't, it means the parent died and the child got reparented to the nearest subreaper, and the function calls raise(signo).