aristanetworks / bst

A one-stop shop for process isolation
MIT License
99 stars 9 forks source link

sig: check parent process liveness with fd read #78

Closed Snaipe closed 1 year ago

Snaipe commented 1 year ago

79f40f6 (sig: make PDEATHSIG handling non-racy) introduced a failure mode that I hadn't forseen at the time; consider the following:

$ bst --pidfile=/tmp/pid sleep infinity & $ bst --share all=/tmp/pid true

If you strace the second bst invocation, you'll see the following:

... [pid 3475469] prctl(PR_SET_PDEATHSIG, SIGKILL) = 0 [pid 3475469] getppid() = 0 [pid 3475469] tgkill(1, 1, SIGKILL) = 0

Notice that getppid() returns 0 -- this happens because when entering an existing pid namespace, the parent of a non-init process (which exists outside of the namespace) gets replaced by 0. This sometimes confuses processes (especially shells), but is for the most part fairly innocent...

... except for the problem that 79f40f6 aimed to fix. The parent gets its own pid with getpid(), but that value has no chance of being the same as getppid() when the child is in the pid namespace. I'm not sure how it even managed to work, but I think we got saved by init being able to get an accurate value for getppid().

In any case, this was the wrong approach. A more reliable way of detecting whether your parent died, is to simply arrange for said parent to set up a nonblocking pipe, and do nothing with it. When reading from the pipe in the child, one of two things can then happen: either the read succeeds with EOF, meaning the pipe was closed by the kernel on process death, or the read fails with EAGAIN, which means the process is still alive.