init: fix "while true; do reboot; done" bug. +15 bytes. Closes bug 781
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 14 Dec 2009 02:03:29 +0000 (03:03 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 14 Dec 2009 02:03:29 +0000 (03:03 +0100)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
init/init.c

index 3748a15..89bbafd 100644 (file)
@@ -260,6 +260,20 @@ static int open_stdio_to_tty(const char* tty_name)
        return 1; /* success */
 }
 
+static void reset_sighandlers_and_unblock_sigs(void)
+{
+       bb_signals(0
+               + (1 << SIGUSR1)
+               + (1 << SIGUSR2)
+               + (1 << SIGTERM)
+               + (1 << SIGQUIT)
+               + (1 << SIGINT)
+               + (1 << SIGHUP)
+               + (1 << SIGTSTP)
+               , SIG_DFL);
+       sigprocmask_allsigs(SIG_UNBLOCK);
+}
+
 /* Wrapper around exec:
  * Takes string (max COMMAND_SIZE chars).
  * If chars like '>' detected, execs '[-]/bin/sh -c "exec ......."'.
@@ -329,16 +343,7 @@ static pid_t run(const struct init_action *a)
        /* Child */
 
        /* Reset signal handlers that were set by the parent process */
-       bb_signals(0
-               + (1 << SIGUSR1)
-               + (1 << SIGUSR2)
-               + (1 << SIGTERM)
-               + (1 << SIGQUIT)
-               + (1 << SIGINT)
-               + (1 << SIGHUP)
-               + (1 << SIGTSTP)
-               , SIG_DFL);
-       sigprocmask_allsigs(SIG_UNBLOCK);
+       reset_sighandlers_and_unblock_sigs();
 
        /* Create a new session and make ourself the process group leader */
        setsid();
@@ -651,12 +656,21 @@ static void run_shutdown_and_kill_processes(void)
  * and only one will be remembered and acted upon.
  */
 
+/* The SIGUSR[12]/SIGTERM handler */
 static void halt_reboot_pwoff(int sig) NORETURN;
 static void halt_reboot_pwoff(int sig)
 {
        const char *m;
        unsigned rb;
 
+       /* We may call run() and it unmasks signals,
+        * including the one masked inside this signal handler.
+        * Testcase which would start multiple reboot scripts:
+        *  while true; do reboot; done
+        * Preventing it:
+        */
+       reset_sighandlers_and_unblock_sigs();
+
        run_shutdown_and_kill_processes();
 
        m = "halt";
@@ -673,38 +687,6 @@ static void halt_reboot_pwoff(int sig)
        /* not reached */
 }
 
-/* The SIGSTOP/SIGTSTP handler
- * NB: inside it, all signals except SIGCONT are masked
- * via appropriate setup in sigaction().
- */
-static void stop_handler(int sig UNUSED_PARAM)
-{
-       smallint saved_bb_got_signal;
-       int saved_errno;
-
-       saved_bb_got_signal = bb_got_signal;
-       saved_errno = errno;
-       signal(SIGCONT, record_signo);
-
-       while (1) {
-               pid_t wpid;
-
-               if (bb_got_signal == SIGCONT)
-                       break;
-               /* NB: this can accidentally wait() for a process
-                * which we waitfor() elsewhere! waitfor() must have
-                * code which is resilient against this.
-                */
-               wpid = wait_any_nohang(NULL);
-               mark_terminated(wpid);
-               sleep(1);
-       }
-
-       signal(SIGCONT, SIG_DFL);
-       errno = saved_errno;
-       bb_got_signal = saved_bb_got_signal;
-}
-
 /* Handler for QUIT - exec "restart" action,
  * else (no such action defined) do nothing */
 static void restart_handler(int sig UNUSED_PARAM)
@@ -719,6 +701,9 @@ static void restart_handler(int sig UNUSED_PARAM)
                 * Thus don't need to worry about preserving errno
                 * and such.
                 */
+
+               reset_sighandlers_and_unblock_sigs();
+
                run_shutdown_and_kill_processes();
 
                /* Allow Ctrl-Alt-Del to reboot the system.
@@ -744,6 +729,38 @@ static void restart_handler(int sig UNUSED_PARAM)
        }
 }
 
+/* The SIGSTOP/SIGTSTP handler
+ * NB: inside it, all signals except SIGCONT are masked
+ * via appropriate setup in sigaction().
+ */
+static void stop_handler(int sig UNUSED_PARAM)
+{
+       smallint saved_bb_got_signal;
+       int saved_errno;
+
+       saved_bb_got_signal = bb_got_signal;
+       saved_errno = errno;
+       signal(SIGCONT, record_signo);
+
+       while (1) {
+               pid_t wpid;
+
+               if (bb_got_signal == SIGCONT)
+                       break;
+               /* NB: this can accidentally wait() for a process
+                * which we waitfor() elsewhere! waitfor() must have
+                * code which is resilient against this.
+                */
+               wpid = wait_any_nohang(NULL);
+               mark_terminated(wpid);
+               sleep(1);
+       }
+
+       signal(SIGCONT, SIG_DFL);
+       errno = saved_errno;
+       bb_got_signal = saved_bb_got_signal;
+}
+
 #if ENABLE_FEATURE_USE_INITTAB
 static void reload_inittab(void)
 {