From bcc6ec9b7e19087254ef323bcc503bb8b6872d75 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 14 Dec 2009 03:03:29 +0100 Subject: init: fix "while true; do reboot; done" bug. +15 bytes. Closes bug 781 Signed-off-by: Denys Vlasenko --- init/init.c | 101 +++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/init/init.c b/init/init.c index 3748a1507..89bbafd13 100644 --- a/init/init.c +++ b/init/init.c @@ -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) { -- cgit v1.2.3