Race in reboot/poweroff path at init?

Laurent Bercot ska-dietlibc at skarnet.org
Tue Oct 10 14:36:47 UTC 2017


>- using a synchronous channel to send the shutdown/reboot message
>   between the poweroff/reboot helpers, rather than an asynchronous
>   signal. Say, have init listening on a socket, allowing the poweroff
>   binary to wait and/or retry.

  That would not work either: you could receive the event before init
starts listening to the socket.

  There will always be a window where init can't receive events. The
kernel starts it barebones, with no channel of communication to other
processes; if an event arrives before it starts establishing these 
channels,
you're out of luck. The best you can do is make the window as small as
possible.

  If you need to be 100% safe, then you need to somehow queue the events
before init starts processing them. But that's tricky, because it's
extremely early - you have nothing but the kernel and the /sbin/poweroff
process' memory. You don't even have the guarantee that you can write to
a filesystem: you only have the rootfs and it may be read-only. You 
don't
even have a tmpfs yet. You can't be certain you have a devtmpfs mounted.
You don't have /dev/shm. You don't have /proc.

  So it's a matter of finding a way to queue events that don't involve
writing to the filesystem at all. That severely restricts your options:
for instance, POSIX message queues sound like a perfect fit, but Linux
implements them via a virtual filesystem that needs to be mounted first,
so it's a no-go.
  Signals are actually pretty good: all they require is that init has
installed a handler, which can be done early. The only issue is that
you can't queue them.

  What I would do is add a check to /sbin/poweroff that init has 
progressed
to a point where its signal handlers are installed, and if it's not
there yet, poll until it is (i.e. sleep and retry).

  What check to use? well at this point it's very hackish. The only
thing I can think of that doesn't depend on the contents of /etc/inittab
is that when init reaps zombies, we know it has its signal handlers
installed. So... I would have poweroff doublefork a process (have the
child communicate the pid of the grandchild before dying), the 
grandchild
dies - at this point it's a zombie waiting for init to reap it - and
poweroff repeatedly hits the grandchild with kill(), using signal 0 just
to be safe. When kill() fails with ESRCH, it means the zombie has
disappeared and init is now ready to accept signals.

  It's really ugly, but it's the best I can come up with that makes no
unsafe assumptions. Whether implementing that in /sbin/poweroff is
better than simply eating the race condition... that's your call.

--
  Laurent



More information about the busybox mailing list