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 | |
| 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>
| -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 | { |
