From 9e5ce59a21ed19a3829bae0b27d957c5fd0de74f Mon Sep 17 00:00:00 2001 From: Arusekk Date: Thu, 16 Jun 2022 19:08:08 +0200 Subject: [PATCH] start-stop-daemon: use vfork to avoid races While running `rc-service start docker` on Gentoo, I found that the command does not start the service 90% of the time, with an enigmatic 'service crashed' message. The root cause of this is apparently rc-service spawning a pty, running start-stop-daemon inside that pty, and exitting, before start-stop-daemon child process calls setsid(), which results in the child process being killed with SIGHUP (SI_KERNEL). Theoretically this bug was present ever since the file was created in 5af58b45146a ("Rewrite the core parts in C. We now provide...") (or even before that), but it should have been only a minor issue before 45bd125dccdc ("Use a pty for prefixed output instead of pipes for..."). Not sure why nobody has had the issue so far (it has been present for almost 15 years). As here setsid() is the last call before execve(), the most natural locking mechanism is vfork(), as it gives back control to parent process only after execve() or process termination. So this way the bug can be fixed by adding a single letter. :-) Another way to ensure this would be using an O_CLOEXEC file descriptor or some custom lock, which would need to be released not before setsid(). Fixes: 5af58b45146a ("Rewrite the core parts in C. We now provide...") Fixes #532. --- src/start-stop-daemon/start-stop-daemon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/start-stop-daemon/start-stop-daemon.c b/src/start-stop-daemon/start-stop-daemon.c index 9117f92f..a5f1f866 100644 --- a/src/start-stop-daemon/start-stop-daemon.c +++ b/src/start-stop-daemon/start-stop-daemon.c @@ -864,8 +864,8 @@ int main(int argc, char **argv) if (background) signal_setup(SIGCHLD, handle_signal); - if ((pid = fork()) == -1) - eerrorx("%s: fork: %s", applet, strerror(errno)); + if ((pid = vfork()) == -1) + eerrorx("%s: vfork: %s", applet, strerror(errno)); /* Child process - lets go! */ if (pid == 0) {