aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2019-12-03 13:48:55 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2019-12-03 14:05:32 +0100
commit356f23de20b48382f5a2c5db29dc4f6dc9d10289 (patch)
tree9e1658b4c6930c713f7fe417832a586a2b9e5a55
parent113c776f4d2ce4ead7c2d11a3ca62adeec9a2e34 (diff)
downloadbusybox-w32-356f23de20b48382f5a2c5db29dc4f6dc9d10289.tar.gz
busybox-w32-356f23de20b48382f5a2c5db29dc4f6dc9d10289.tar.bz2
busybox-w32-356f23de20b48382f5a2c5db29dc4f6dc9d10289.zip
init: improve handling of signals racing with each other
Before this change, a request to reboot could be "overwritten" by e.g. SIGHUP. function old new delta init_main 709 793 +84 packed_usage 33273 33337 +64 run_actions 109 117 +8 stop_handler 87 88 +1 check_delayed_sigs 340 335 -5 run 214 198 -16 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/2 up/down: 157/-21) Total: 136 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--init/init.c278
1 files changed, 119 insertions, 159 deletions
diff --git a/init/init.c b/init/init.c
index db1d99add..28775a65c 100644
--- a/init/init.c
+++ b/init/init.c
@@ -210,6 +210,8 @@ struct globals {
210#if !ENABLE_FEATURE_INIT_SYSLOG 210#if !ENABLE_FEATURE_INIT_SYSLOG
211 const char *log_console; 211 const char *log_console;
212#endif 212#endif
213 sigset_t delayed_sigset;
214 struct timespec zero_ts;
213} FIX_ALIASING; 215} FIX_ALIASING;
214#define G (*(struct globals*)bb_common_bufsiz1) 216#define G (*(struct globals*)bb_common_bufsiz1)
215#define INIT_G() do { \ 217#define INIT_G() do { \
@@ -411,14 +413,8 @@ static int open_stdio_to_tty(const char* tty_name)
411static void reset_sighandlers_and_unblock_sigs(void) 413static void reset_sighandlers_and_unblock_sigs(void)
412{ 414{
413 bb_signals(0 415 bb_signals(0
414 + (1 << SIGUSR1) 416 | (1 << SIGTSTP)
415 + (1 << SIGUSR2) 417 | (1 << SIGSTOP)
416 + (1 << SIGTERM)
417 + (1 << SIGQUIT)
418 + (1 << SIGINT)
419 + (1 << SIGHUP)
420 + (1 << SIGTSTP)
421 + (1 << SIGSTOP)
422 , SIG_DFL); 418 , SIG_DFL);
423 sigprocmask_allsigs(SIG_UNBLOCK); 419 sigprocmask_allsigs(SIG_UNBLOCK);
424} 420}
@@ -484,16 +480,13 @@ static pid_t run(const struct init_action *a)
484{ 480{
485 pid_t pid; 481 pid_t pid;
486 482
487 /* Careful: don't be affected by a signal in vforked child */
488 sigprocmask_allsigs(SIG_BLOCK);
489 if (BB_MMU && (a->action_type & ASKFIRST)) 483 if (BB_MMU && (a->action_type & ASKFIRST))
490 pid = fork(); 484 pid = fork();
491 else 485 else
492 pid = vfork(); 486 pid = vfork();
493 if (pid < 0)
494 message(L_LOG | L_CONSOLE, "can't fork");
495 if (pid) { 487 if (pid) {
496 sigprocmask_allsigs(SIG_UNBLOCK); 488 if (pid < 0)
489 message(L_LOG | L_CONSOLE, "can't fork");
497 return pid; /* Parent or error */ 490 return pid; /* Parent or error */
498 } 491 }
499 492
@@ -587,9 +580,11 @@ static void waitfor(pid_t pid)
587 while (1) { 580 while (1) {
588 pid_t wpid = wait(NULL); 581 pid_t wpid = wait(NULL);
589 mark_terminated(wpid); 582 mark_terminated(wpid);
590 /* Unsafe. SIGTSTP handler might have wait'ed it already */ 583 if (wpid == pid) /* this was the process we waited for */
591 /*if (wpid == pid) break;*/ 584 break;
592 /* More reliable: */ 585 /* The above is not reliable enough: SIGTSTP handler might have
586 * wait'ed it already. Double check, exit if process is gone:
587 */
593 if (kill(pid, 0)) 588 if (kill(pid, 0))
594 break; 589 break;
595 } 590 }
@@ -798,23 +793,17 @@ static void run_shutdown_and_kill_processes(void)
798 * Delayed handlers just set a flag variable. The variable is checked 793 * Delayed handlers just set a flag variable. The variable is checked
799 * in the main loop and acted upon. 794 * in the main loop and acted upon.
800 * 795 *
801 * halt/poweroff/reboot and restart have immediate handlers.
802 * They only traverse linked list of struct action's, never modify it,
803 * this should be safe to do even in signal handler. Also they
804 * never return.
805 *
806 * SIGSTOP and SIGTSTP have immediate handlers. They just wait 796 * SIGSTOP and SIGTSTP have immediate handlers. They just wait
807 * for SIGCONT to happen. 797 * for SIGCONT to happen.
808 * 798 *
799 * halt/poweroff/reboot and restart have delayed handlers.
800 *
809 * SIGHUP has a delayed handler, because modifying linked list 801 * SIGHUP has a delayed handler, because modifying linked list
810 * of struct action's from a signal handler while it is manipulated 802 * of struct action's from a signal handler while it is manipulated
811 * by the program may be disastrous. 803 * by the program may be disastrous.
812 * 804 *
813 * Ctrl-Alt-Del has a delayed handler. Not a must, but allowing 805 * Ctrl-Alt-Del has a delayed handler. Not a must, but allowing
814 * it to happen even somewhere inside "sysinit" would be a bit awkward. 806 * it to happen even somewhere inside "sysinit" would be a bit awkward.
815 *
816 * There is a tiny probability that SIGHUP and Ctrl-Alt-Del will collide
817 * and only one will be remembered and acted upon.
818 */ 807 */
819 808
820/* The SIGPWR/SIGUSR[12]/SIGTERM handler */ 809/* The SIGPWR/SIGUSR[12]/SIGTERM handler */
@@ -898,11 +887,9 @@ static void exec_restart_action(void)
898 */ 887 */
899static void stop_handler(int sig UNUSED_PARAM) 888static void stop_handler(int sig UNUSED_PARAM)
900{ 889{
901 smallint saved_bb_got_signal; 890 int saved_errno = errno;
902 int saved_errno;
903 891
904 saved_bb_got_signal = bb_got_signal; 892 bb_got_signal = 0;
905 saved_errno = errno;
906 signal(SIGCONT, record_signo); 893 signal(SIGCONT, record_signo);
907 894
908 while (1) { 895 while (1) {
@@ -916,12 +903,12 @@ static void stop_handler(int sig UNUSED_PARAM)
916 */ 903 */
917 wpid = wait_any_nohang(NULL); 904 wpid = wait_any_nohang(NULL);
918 mark_terminated(wpid); 905 mark_terminated(wpid);
919 sleep(1); 906 if (wpid <= 0) /* no processes exited? sleep a bit */
907 sleep(1);
920 } 908 }
921 909
922 signal(SIGCONT, SIG_DFL); 910 signal(SIGCONT, SIG_DFL);
923 errno = saved_errno; 911 errno = saved_errno;
924 bb_got_signal = saved_bb_got_signal;
925} 912}
926 913
927#if ENABLE_FEATURE_USE_INITTAB 914#if ENABLE_FEATURE_USE_INITTAB
@@ -986,43 +973,39 @@ static void reload_inittab(void)
986} 973}
987#endif 974#endif
988 975
989static int check_delayed_sigs(void) 976static void check_delayed_sigs(struct timespec *ts)
990{ 977{
991 int sigs_seen = 0; 978 int sig = sigtimedwait(&G.delayed_sigset, /* siginfo_t */ NULL, ts);
979 if (sig <= 0)
980 return;
992 981
993 while (1) { 982 /* The signal "sig" was caught */
994 smallint sig = bb_got_signal;
995 983
996 if (!sig)
997 return sigs_seen;
998 bb_got_signal = 0;
999 sigs_seen = 1;
1000#if ENABLE_FEATURE_USE_INITTAB 984#if ENABLE_FEATURE_USE_INITTAB
1001 if (sig == SIGHUP) 985 if (sig == SIGHUP)
1002 reload_inittab(); 986 reload_inittab();
1003#endif 987#endif
1004 if (sig == SIGINT) 988 if (sig == SIGINT)
1005 run_actions(CTRLALTDEL); 989 run_actions(CTRLALTDEL);
1006 if (sig == SIGQUIT) { 990 if (sig == SIGQUIT) {
1007 exec_restart_action(); 991 exec_restart_action();
1008 /* returns only if no restart action defined */ 992 /* returns only if no restart action defined */
1009 } 993 }
1010 if ((1 << sig) & (0 994 if ((1 << sig) & (0
1011#ifdef SIGPWR 995#ifdef SIGPWR
1012 + (1 << SIGPWR) 996 | (1 << SIGPWR)
1013#endif 997#endif
1014 + (1 << SIGUSR1) 998 | (1 << SIGUSR1)
1015 + (1 << SIGUSR2) 999 | (1 << SIGUSR2)
1016 + (1 << SIGTERM) 1000 | (1 << SIGTERM)
1017 )) { 1001 )) {
1018 halt_reboot_pwoff(sig); 1002 halt_reboot_pwoff(sig);
1019 }
1020 } 1003 }
1004 /* if (sig == SIGCHLD) do nothing */
1021} 1005}
1022 1006
1023#if DEBUG_SEGV_HANDLER 1007#if DEBUG_SEGV_HANDLER
1024static 1008static void handle_sigsegv(int sig, siginfo_t *info, void *ucontext)
1025void handle_sigsegv(int sig, siginfo_t *info, void *ucontext)
1026{ 1009{
1027 long ip; 1010 long ip;
1028 ucontext_t *uc; 1011 ucontext_t *uc;
@@ -1049,50 +1032,62 @@ void handle_sigsegv(int sig, siginfo_t *info, void *ucontext)
1049 1032
1050static void sleep_much(void) 1033static void sleep_much(void)
1051{ 1034{
1052 sleep(30 * 24*60*60); 1035 sleep(30 * 24*60*60);
1053} 1036}
1054 1037
1055int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1038int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1056int init_main(int argc UNUSED_PARAM, char **argv) 1039int init_main(int argc UNUSED_PARAM, char **argv)
1057{ 1040{
1041 struct sigaction sa;
1042
1058 INIT_G(); 1043 INIT_G();
1059 1044
1045 /* Some users send poweroff signals to init VERY early.
1046 * To handle this, mask signals early.
1047 */
1048 /* sigemptyset(&G.delayed_sigset); - done by INIT_G() */
1049 sigaddset(&G.delayed_sigset, SIGINT); /* Ctrl-Alt-Del */
1050 sigaddset(&G.delayed_sigset, SIGQUIT); /* re-exec another init */
1051#ifdef SIGPWR
1052 sigaddset(&G.delayed_sigset, SIGPWR); /* halt */
1053#endif
1054 sigaddset(&G.delayed_sigset, SIGUSR1); /* halt */
1055 sigaddset(&G.delayed_sigset, SIGTERM); /* reboot */
1056 sigaddset(&G.delayed_sigset, SIGUSR2); /* poweroff */
1057#if ENABLE_FEATURE_USE_INITTAB
1058 sigaddset(&G.delayed_sigset, SIGHUP); /* reread /etc/inittab */
1059#endif
1060 sigaddset(&G.delayed_sigset, SIGCHLD); /* make sigtimedwait() exit on SIGCHLD */
1061 sigprocmask(SIG_BLOCK, &G.delayed_sigset, NULL);
1062
1063#if DEBUG_SEGV_HANDLER
1064 memset(&sa, 0, sizeof(sa));
1065 sa.sa_sigaction = handle_sigsegv;
1066 sa.sa_flags = SA_SIGINFO;
1067 sigaction_set(SIGSEGV, &sa);
1068 sigaction_set(SIGILL, &sa);
1069 sigaction_set(SIGFPE, &sa);
1070 sigaction_set(SIGBUS, &sa);
1071#endif
1072
1060 if (argv[1] && strcmp(argv[1], "-q") == 0) { 1073 if (argv[1] && strcmp(argv[1], "-q") == 0) {
1061 return kill(1, SIGHUP); 1074 return kill(1, SIGHUP);
1062 } 1075 }
1063 1076
1064#if DEBUG_SEGV_HANDLER 1077#if !DEBUG_INIT
1065 { 1078 /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */
1066 struct sigaction sa; 1079 if (getpid() != 1
1067 memset(&sa, 0, sizeof(sa)); 1080 && (!ENABLE_LINUXRC || applet_name[0] != 'l') /* not linuxrc? */
1068 sa.sa_sigaction = handle_sigsegv; 1081 ) {
1069 sa.sa_flags = SA_SIGINFO; 1082 bb_simple_error_msg_and_die("must be run as PID 1");
1070 sigaction(SIGSEGV, &sa, NULL);
1071 sigaction(SIGILL, &sa, NULL);
1072 sigaction(SIGFPE, &sa, NULL);
1073 sigaction(SIGBUS, &sa, NULL);
1074 } 1083 }
1075#endif
1076
1077 if (!DEBUG_INIT) {
1078 /* Some users send poweroff signals to init VERY early.
1079 * To handle this, mask signals early,
1080 * and unmask them only after signal handlers are installed.
1081 */
1082 sigprocmask_allsigs(SIG_BLOCK);
1083 1084
1084 /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ 1085# ifdef RB_DISABLE_CAD
1085 if (getpid() != 1 1086 /* Turn off rebooting via CTL-ALT-DEL - we get a
1086 && (!ENABLE_LINUXRC || applet_name[0] != 'l') /* not linuxrc? */ 1087 * SIGINT on CAD so we can shut things down gracefully... */
1087 ) { 1088 reboot(RB_DISABLE_CAD); /* misnomer */
1088 bb_simple_error_msg_and_die("must be run as PID 1"); 1089# endif
1089 }
1090#ifdef RB_DISABLE_CAD
1091 /* Turn off rebooting via CTL-ALT-DEL - we get a
1092 * SIGINT on CAD so we can shut things down gracefully... */
1093 reboot(RB_DISABLE_CAD); /* misnomer */
1094#endif 1090#endif
1095 }
1096 1091
1097 /* If, say, xmalloc would ever die, we don't want to oops kernel 1092 /* If, say, xmalloc would ever die, we don't want to oops kernel
1098 * by exiting. 1093 * by exiting.
@@ -1156,106 +1151,65 @@ int init_main(int argc UNUSED_PARAM, char **argv)
1156 } 1151 }
1157#endif 1152#endif
1158 1153
1159 if (ENABLE_FEATURE_INIT_MODIFY_CMDLINE) { 1154#if ENABLE_FEATURE_INIT_MODIFY_CMDLINE
1160 /* Make the command line just say "init" - that's all, nothing else */ 1155 /* Make the command line just say "init" - that's all, nothing else */
1161 strncpy(argv[0], "init", strlen(argv[0])); 1156 strncpy(argv[0], "init", strlen(argv[0]));
1162 /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */ 1157 /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
1163 while (*++argv) 1158 while (*++argv)
1164 nuke_str(*argv); 1159 nuke_str(*argv);
1165 }
1166
1167 /* Set up signal handlers */
1168 if (!DEBUG_INIT) {
1169 struct sigaction sa;
1170
1171 /* Stop handler must allow only SIGCONT inside itself */
1172 memset(&sa, 0, sizeof(sa));
1173 sigfillset(&sa.sa_mask);
1174 sigdelset(&sa.sa_mask, SIGCONT);
1175 sa.sa_handler = stop_handler;
1176 /* NB: sa_flags doesn't have SA_RESTART.
1177 * It must be able to interrupt wait().
1178 */
1179 sigaction_set(SIGTSTP, &sa); /* pause */
1180 /* Does not work as intended, at least in 2.6.20.
1181 * SIGSTOP is simply ignored by init:
1182 */
1183 sigaction_set(SIGSTOP, &sa); /* pause */
1184
1185 /* These signals must interrupt wait(),
1186 * setting handler without SA_RESTART flag.
1187 */
1188 bb_signals_recursive_norestart(0
1189 + (1 << SIGINT) /* Ctrl-Alt-Del */
1190 + (1 << SIGQUIT) /* re-exec another init */
1191#ifdef SIGPWR
1192 + (1 << SIGPWR) /* halt */
1193#endif
1194 + (1 << SIGUSR1) /* halt */
1195 + (1 << SIGTERM) /* reboot */
1196 + (1 << SIGUSR2) /* poweroff */
1197#if ENABLE_FEATURE_USE_INITTAB
1198 + (1 << SIGHUP) /* reread /etc/inittab */
1199#endif 1160#endif
1200 , record_signo);
1201 1161
1202 sigprocmask_allsigs(SIG_UNBLOCK); 1162 /* Set up STOP signal handlers */
1203 } 1163 /* Stop handler must allow only SIGCONT inside itself */
1164 memset(&sa, 0, sizeof(sa));
1165 sigfillset(&sa.sa_mask);
1166 sigdelset(&sa.sa_mask, SIGCONT);
1167 sa.sa_handler = stop_handler;
1168 sa.sa_flags = SA_RESTART;
1169 sigaction_set(SIGTSTP, &sa); /* pause */
1170 /* Does not work as intended, at least in 2.6.20.
1171 * SIGSTOP is simply ignored by init
1172 * (NB: behavior might differ under strace):
1173 */
1174 sigaction_set(SIGSTOP, &sa); /* pause */
1204 1175
1205 /* Now run everything that needs to be run */ 1176 /* Now run everything that needs to be run */
1206 /* First run the sysinit command */ 1177 /* First run the sysinit command */
1207 run_actions(SYSINIT); 1178 run_actions(SYSINIT);
1208 check_delayed_sigs(); 1179 check_delayed_sigs(&G.zero_ts);
1209 /* Next run anything that wants to block */ 1180 /* Next run anything that wants to block */
1210 run_actions(WAIT); 1181 run_actions(WAIT);
1211 check_delayed_sigs(); 1182 check_delayed_sigs(&G.zero_ts);
1212 /* Next run anything to be run only once */ 1183 /* Next run anything to be run only once */
1213 run_actions(ONCE); 1184 run_actions(ONCE);
1214 1185
1215 /* Now run the looping stuff for the rest of forever. 1186 /* Now run the looping stuff for the rest of forever */
1216 */
1217 while (1) { 1187 while (1) {
1218 int maybe_WNOHANG;
1219
1220 maybe_WNOHANG = check_delayed_sigs();
1221
1222 /* (Re)run the respawn/askfirst stuff */ 1188 /* (Re)run the respawn/askfirst stuff */
1223 run_actions(RESPAWN | ASKFIRST); 1189 run_actions(RESPAWN | ASKFIRST);
1224 maybe_WNOHANG |= check_delayed_sigs();
1225 1190
1226 /* Don't consume all CPU time - sleep a bit */ 1191 /* Wait for any signal (typically it's SIGCHLD) */
1227 sleep(1); 1192 check_delayed_sigs(NULL); /* NULL timespec makes it wait */
1228 maybe_WNOHANG |= check_delayed_sigs(); 1193
1229 1194 /* Wait for any child process(es) to exit */
1230 /* Wait for any child process(es) to exit.
1231 *
1232 * If check_delayed_sigs above reported that a signal
1233 * was caught, wait will be nonblocking. This ensures
1234 * that if SIGHUP has reloaded inittab, respawn and askfirst
1235 * actions will not be delayed until next child death.
1236 */
1237 if (maybe_WNOHANG)
1238 maybe_WNOHANG = WNOHANG;
1239 while (1) { 1195 while (1) {
1240 pid_t wpid; 1196 pid_t wpid;
1241 struct init_action *a; 1197 struct init_action *a;
1242 1198
1243 /* If signals happen _in_ the wait, they interrupt it, 1199 wpid = waitpid(-1, NULL, WNOHANG);
1244 * bb_signals_recursive_norestart set them up that way
1245 */
1246 wpid = waitpid(-1, NULL, maybe_WNOHANG);
1247 if (wpid <= 0) 1200 if (wpid <= 0)
1248 break; 1201 break;
1249 1202
1250 a = mark_terminated(wpid); 1203 a = mark_terminated(wpid);
1251 if (a) { 1204 if (a) {
1252 message(L_LOG, "process '%s' (pid %d) exited. " 1205 message(L_LOG, "process '%s' (pid %u) exited. "
1253 "Scheduling for restart.", 1206 "Scheduling for restart.",
1254 a->command, wpid); 1207 a->command, (unsigned)wpid);
1255 } 1208 }
1256 /* See if anyone else is waiting to be reaped */
1257 maybe_WNOHANG = WNOHANG;
1258 } 1209 }
1210
1211 /* Don't consume all CPU time - sleep a bit */
1212 sleep(1);
1259 } /* while (1) */ 1213 } /* while (1) */
1260} 1214}
1261 1215
@@ -1268,11 +1222,17 @@ int init_main(int argc UNUSED_PARAM, char **argv)
1268//usage: "Init is the first process started during boot. It never exits." 1222//usage: "Init is the first process started during boot. It never exits."
1269//usage: IF_FEATURE_USE_INITTAB( 1223//usage: IF_FEATURE_USE_INITTAB(
1270//usage: "\n""It (re)spawns children according to /etc/inittab." 1224//usage: "\n""It (re)spawns children according to /etc/inittab."
1225//usage: "\n""Signals:"
1226//usage: "\n""HUP: reload /etc/inittab"
1271//usage: ) 1227//usage: )
1272//usage: IF_NOT_FEATURE_USE_INITTAB( 1228//usage: IF_NOT_FEATURE_USE_INITTAB(
1273//usage: "\n""This version of init doesn't use /etc/inittab," 1229//usage: "\n""This version of init doesn't use /etc/inittab,"
1274//usage: "\n""has fixed set of processed to run." 1230//usage: "\n""has fixed set of processed to run."
1231//usage: "\n""Signals:"
1275//usage: ) 1232//usage: )
1233//usage: "\n""TSTP: stop respawning until CONT"
1234//usage: "\n""QUIT: re-exec another init"
1235//usage: "\n""USR1/TERM/USR2/INT: run halt/reboot/poweroff/Ctrl-Alt-Del script"
1276//usage: 1236//usage:
1277//usage:#define init_notes_usage 1237//usage:#define init_notes_usage
1278//usage: "This version of init is designed to be run only by the kernel.\n" 1238//usage: "This version of init is designed to be run only by the kernel.\n"