Race in reboot/poweroff path at init?

Ralf Friedl Ralf.Friedl at online.de
Wed Oct 11 10:17:05 UTC 2017


Laurent Bercot wrote:
>  That is true, I hadn't thought of abstract sockets, and it would work.
>
>  However, changing the way poweroff signals init is a big change, and
> in particular, making init listen to a socket is very significant: you
> know need to multiplex reaping zombies with listening to connections.
> It's not hard, but it requires patching init significantly, reworking
> its control flow around an asynchronous event loop; and at that point
> you're just better off using a better pid 1 process (such as s6-svscan,
> which already does this - have you considered switching inits? ;)). And
> changes of that magnitude are pretty dangerous - I'm quite convinced
> there are some people relying on the "send a signal to init" mechanism
> and whose systems would break if that mechanism were changed.
>
>  The "send a signal to init" mechanism is a good one, and does not
> fundamentally need to be changed; your problem is a readiness detection
> one, i.e. whatever mechanism you're using to tell init to shutdown, you
> can't use it before init is ready to handle it.
>
>  And that's where abstract sockets can be useful: you can use abstract
> sockets as a simple synchronization mechanism. The easiest way to do it
> would be: when init has installed its signal handlers, it creates an
> abstract socket (close-on-exec, of course) and binds it - but does not
> listen to it and does nothing else with it). And poweroff checks for
> the presence of this socket by repeatedly attempting connect(). When
> errno changes from ENOENT to ECONNREFUSED, it means the socket has been
> created and init is now ready to receive signals.
>
>  This requires minimal change to init and a small polling addition
> to poweroff - which I think is much better and safer than a heavy change
> to busybox's init.
I think your idea with wait is better. It doesn't require any additional 
code to the init process, it is likely more portable, it doesn't consume 
memory for an open socket and the changes to poweroff are also smaller.
I would modify it so that it doesn't check for the presence of the 
grandchild, as the grandchild may be started from a process that does 
not do wait. Just check whether the parent PID is 1 or not.

Basically:
if ((pid1 = fork ()) > 0) exit (0);
if ((pid2 = fork ()) > 0) exit (0);
while (pid1 > 0 && pid2 > 0 && getppid () != 1) // only wait if both 
fork calls were successful
   sleep (1);
If fork fails, it should signal init from the parent process. If there 
are not enough resources to create a new process, we can safely assume 
that init is ready to receive a signal.


More information about the busybox mailing list