aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArusekk <arek_koz@o2.pl>2022-06-16 19:08:08 +0200
committerWilliam Hubbs <w.d.hubbs@gmail.com>2022-09-02 12:41:09 -0500
commit9e5ce59a21ed19a3829bae0b27d957c5fd0de74f (patch)
tree4ab11cb38d79bb663bfb1734c282863bfca07999
parent95dc83bfbcfeffd1f1dca97e1c5b9a346e0ea42e (diff)
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.
-rw-r--r--src/start-stop-daemon/start-stop-daemon.c4
1 files 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) {