diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-12-14 03:03:29 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-12-14 03:03:29 +0100 |
commit | bcc6ec9b7e19087254ef323bcc503bb8b6872d75 (patch) | |
tree | 2f9bc3f4e1735a773602d667a0a6137278de70d4 /init | |
parent | 6be918d0ae15ca5f8b3b3879c496205bc8a11c2b (diff) | |
download | busybox-w32-bcc6ec9b7e19087254ef323bcc503bb8b6872d75.tar.gz busybox-w32-bcc6ec9b7e19087254ef323bcc503bb8b6872d75.tar.bz2 busybox-w32-bcc6ec9b7e19087254ef323bcc503bb8b6872d75.zip |
init: fix "while true; do reboot; done" bug. +15 bytes. Closes bug 781
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'init')
-rw-r--r-- | init/init.c | 101 |
1 files 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) | |||
260 | return 1; /* success */ | 260 | return 1; /* success */ |
261 | } | 261 | } |
262 | 262 | ||
263 | static void reset_sighandlers_and_unblock_sigs(void) | ||
264 | { | ||
265 | bb_signals(0 | ||
266 | + (1 << SIGUSR1) | ||
267 | + (1 << SIGUSR2) | ||
268 | + (1 << SIGTERM) | ||
269 | + (1 << SIGQUIT) | ||
270 | + (1 << SIGINT) | ||
271 | + (1 << SIGHUP) | ||
272 | + (1 << SIGTSTP) | ||
273 | , SIG_DFL); | ||
274 | sigprocmask_allsigs(SIG_UNBLOCK); | ||
275 | } | ||
276 | |||
263 | /* Wrapper around exec: | 277 | /* Wrapper around exec: |
264 | * Takes string (max COMMAND_SIZE chars). | 278 | * Takes string (max COMMAND_SIZE chars). |
265 | * If chars like '>' detected, execs '[-]/bin/sh -c "exec ......."'. | 279 | * If chars like '>' detected, execs '[-]/bin/sh -c "exec ......."'. |
@@ -329,16 +343,7 @@ static pid_t run(const struct init_action *a) | |||
329 | /* Child */ | 343 | /* Child */ |
330 | 344 | ||
331 | /* Reset signal handlers that were set by the parent process */ | 345 | /* Reset signal handlers that were set by the parent process */ |
332 | bb_signals(0 | 346 | reset_sighandlers_and_unblock_sigs(); |
333 | + (1 << SIGUSR1) | ||
334 | + (1 << SIGUSR2) | ||
335 | + (1 << SIGTERM) | ||
336 | + (1 << SIGQUIT) | ||
337 | + (1 << SIGINT) | ||
338 | + (1 << SIGHUP) | ||
339 | + (1 << SIGTSTP) | ||
340 | , SIG_DFL); | ||
341 | sigprocmask_allsigs(SIG_UNBLOCK); | ||
342 | 347 | ||
343 | /* Create a new session and make ourself the process group leader */ | 348 | /* Create a new session and make ourself the process group leader */ |
344 | setsid(); | 349 | setsid(); |
@@ -651,12 +656,21 @@ static void run_shutdown_and_kill_processes(void) | |||
651 | * and only one will be remembered and acted upon. | 656 | * and only one will be remembered and acted upon. |
652 | */ | 657 | */ |
653 | 658 | ||
659 | /* The SIGUSR[12]/SIGTERM handler */ | ||
654 | static void halt_reboot_pwoff(int sig) NORETURN; | 660 | static void halt_reboot_pwoff(int sig) NORETURN; |
655 | static void halt_reboot_pwoff(int sig) | 661 | static void halt_reboot_pwoff(int sig) |
656 | { | 662 | { |
657 | const char *m; | 663 | const char *m; |
658 | unsigned rb; | 664 | unsigned rb; |
659 | 665 | ||
666 | /* We may call run() and it unmasks signals, | ||
667 | * including the one masked inside this signal handler. | ||
668 | * Testcase which would start multiple reboot scripts: | ||
669 | * while true; do reboot; done | ||
670 | * Preventing it: | ||
671 | */ | ||
672 | reset_sighandlers_and_unblock_sigs(); | ||
673 | |||
660 | run_shutdown_and_kill_processes(); | 674 | run_shutdown_and_kill_processes(); |
661 | 675 | ||
662 | m = "halt"; | 676 | m = "halt"; |
@@ -673,38 +687,6 @@ static void halt_reboot_pwoff(int sig) | |||
673 | /* not reached */ | 687 | /* not reached */ |
674 | } | 688 | } |
675 | 689 | ||
676 | /* The SIGSTOP/SIGTSTP handler | ||
677 | * NB: inside it, all signals except SIGCONT are masked | ||
678 | * via appropriate setup in sigaction(). | ||
679 | */ | ||
680 | static void stop_handler(int sig UNUSED_PARAM) | ||
681 | { | ||
682 | smallint saved_bb_got_signal; | ||
683 | int saved_errno; | ||
684 | |||
685 | saved_bb_got_signal = bb_got_signal; | ||
686 | saved_errno = errno; | ||
687 | signal(SIGCONT, record_signo); | ||
688 | |||
689 | while (1) { | ||
690 | pid_t wpid; | ||
691 | |||
692 | if (bb_got_signal == SIGCONT) | ||
693 | break; | ||
694 | /* NB: this can accidentally wait() for a process | ||
695 | * which we waitfor() elsewhere! waitfor() must have | ||
696 | * code which is resilient against this. | ||
697 | */ | ||
698 | wpid = wait_any_nohang(NULL); | ||
699 | mark_terminated(wpid); | ||
700 | sleep(1); | ||
701 | } | ||
702 | |||
703 | signal(SIGCONT, SIG_DFL); | ||
704 | errno = saved_errno; | ||
705 | bb_got_signal = saved_bb_got_signal; | ||
706 | } | ||
707 | |||
708 | /* Handler for QUIT - exec "restart" action, | 690 | /* Handler for QUIT - exec "restart" action, |
709 | * else (no such action defined) do nothing */ | 691 | * else (no such action defined) do nothing */ |
710 | static void restart_handler(int sig UNUSED_PARAM) | 692 | static void restart_handler(int sig UNUSED_PARAM) |
@@ -719,6 +701,9 @@ static void restart_handler(int sig UNUSED_PARAM) | |||
719 | * Thus don't need to worry about preserving errno | 701 | * Thus don't need to worry about preserving errno |
720 | * and such. | 702 | * and such. |
721 | */ | 703 | */ |
704 | |||
705 | reset_sighandlers_and_unblock_sigs(); | ||
706 | |||
722 | run_shutdown_and_kill_processes(); | 707 | run_shutdown_and_kill_processes(); |
723 | 708 | ||
724 | /* Allow Ctrl-Alt-Del to reboot the system. | 709 | /* Allow Ctrl-Alt-Del to reboot the system. |
@@ -744,6 +729,38 @@ static void restart_handler(int sig UNUSED_PARAM) | |||
744 | } | 729 | } |
745 | } | 730 | } |
746 | 731 | ||
732 | /* The SIGSTOP/SIGTSTP handler | ||
733 | * NB: inside it, all signals except SIGCONT are masked | ||
734 | * via appropriate setup in sigaction(). | ||
735 | */ | ||
736 | static void stop_handler(int sig UNUSED_PARAM) | ||
737 | { | ||
738 | smallint saved_bb_got_signal; | ||
739 | int saved_errno; | ||
740 | |||
741 | saved_bb_got_signal = bb_got_signal; | ||
742 | saved_errno = errno; | ||
743 | signal(SIGCONT, record_signo); | ||
744 | |||
745 | while (1) { | ||
746 | pid_t wpid; | ||
747 | |||
748 | if (bb_got_signal == SIGCONT) | ||
749 | break; | ||
750 | /* NB: this can accidentally wait() for a process | ||
751 | * which we waitfor() elsewhere! waitfor() must have | ||
752 | * code which is resilient against this. | ||
753 | */ | ||
754 | wpid = wait_any_nohang(NULL); | ||
755 | mark_terminated(wpid); | ||
756 | sleep(1); | ||
757 | } | ||
758 | |||
759 | signal(SIGCONT, SIG_DFL); | ||
760 | errno = saved_errno; | ||
761 | bb_got_signal = saved_bb_got_signal; | ||
762 | } | ||
763 | |||
747 | #if ENABLE_FEATURE_USE_INITTAB | 764 | #if ENABLE_FEATURE_USE_INITTAB |
748 | static void reload_inittab(void) | 765 | static void reload_inittab(void) |
749 | { | 766 | { |