diff options
author | Arusekk <arek_koz@o2.pl> | 2022-06-16 19:08:08 +0200 |
---|---|---|
committer | William Hubbs <w.d.hubbs@gmail.com> | 2022-09-02 12:41:09 -0500 |
commit | 9e5ce59a21ed19a3829bae0b27d957c5fd0de74f (patch) | |
tree | 4ab11cb38d79bb663bfb1734c282863bfca07999 /src | |
parent | 95dc83bfbcfeffd1f1dca97e1c5b9a346e0ea42e (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.
Diffstat (limited to 'src')
-rw-r--r-- | src/start-stop-daemon/start-stop-daemon.c | 4 |
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) { |