NOFORK fixes
No binary (build failed)
—
(log yok)
| @@ -16,7 +16,8 @@ | |
| //config: help | |
| //config: rm is used to remove files or directories. | |
| - | //applet:IF_RM(APPLET_NOFORK(rm, rm, BB_DIR_BIN, BB_SUID_DROP, rm)) |
| + | //applet:IF_RM(APPLET_NOEXEC(rm, rm, BB_DIR_BIN, BB_SUID_DROP, rm)) |
| + | /* was NOFORK, but then "rm -i FILE" can't be ^C'ed if run by hush */ |
| //kbuild:lib-$(CONFIG_RM) += rm.o | |
| @@ -36,7 +37,7 @@ | |
| #include "libbb.h" | |
| - | /* This is a NOFORK applet. Be very careful! */ |
| + | /* This is a NOEXEC applet. Be very careful! */ |
| int rm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |
| int rm_main(int argc UNUSED_PARAM, char **argv) | |
| @@ -12,7 +12,8 @@ | |
| //config: help | |
| //config: print a sequence of numbers | |
| - | //applet:IF_SEQ(APPLET_NOFORK(seq, seq, BB_DIR_USR_BIN, BB_SUID_DROP, seq)) |
| + | //applet:IF_SEQ(APPLET_NOEXEC(seq, seq, BB_DIR_USR_BIN, BB_SUID_DROP, seq)) |
| + | /* was NOFORK, but then "seq 1 999999999" can't be ^C'ed if run by hush */ |
| //kbuild:lib-$(CONFIG_SEQ) += seq.o | |
| @@ -26,7 +27,7 @@ | |
| #include "libbb.h" | |
| - | /* This is a NOFORK applet. Be very careful! */ |
| + | /* This is a NOEXEC applet. Be very careful! */ |
| int seq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |
| int seq_main(int argc, char **argv) | |
| @@ -38,6 +38,13 @@ int usleep_main(int argc UNUSED_PARAM, char **argv) | |
| bb_show_usage(); | |
| } | |
| + | /* Safe wrt NOFORK? (noforks are not allowed to run for |
| + | * a long time). Try "usleep 99999999" + ^C + "echo $?" |
| + | * in hush with FEATURE_SH_NOFORK=y. |
| + | * At least on uclibc, usleep() thanslates to nanosleep() |
| + | * which returns early on any signal (even caught one), |
| + | * and uclibc does not loop back on EINTR. |
| + | */ |
| usleep(xatou(argv[1])); | |
| return EXIT_SUCCESS; | |
| @@ -17,7 +17,8 @@ | |
| //config: yes is used to repeatedly output a specific string, or | |
| //config: the default string 'y'. | |
| - | //applet:IF_YES(APPLET_NOFORK(yes, yes, BB_DIR_USR_BIN, BB_SUID_DROP, yes)) |
| + | //applet:IF_YES(APPLET_NOEXEC(yes, yes, BB_DIR_USR_BIN, BB_SUID_DROP, yes)) |
| + | /* was NOFORK, but then yes can't be ^C'ed if run by hush */ |
| //kbuild:lib-$(CONFIG_YES) += yes.o | |
| @@ -52,6 +52,9 @@ xargs, find, shells do it (grep for "spawn_and_wait" and | |
| This poses much more serious limitations on what applet can do: | |
| * all NOEXEC limitations apply. | |
| + | * do not run for a long time or wait for user input: |
| + | hush shell only handles signals (like ^C) after you return |
| + | from APPLET_main(). |
| * do not ever exit() or exec(). | |
| - xfuncs are okay. They are using special trick to return | |
| to the caller applet instead of dying when they detect "x" condition. | |
| @@ -7363,6 +7363,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |
| */ | |
| close_saved_fds_and_FILE_fds(); | |
| //FIXME: should also close saved redir fds | |
| + | /* Without this, "rm -i FILE" can't be ^C'ed: */ |
| + | switch_off_special_sigs(G.special_sig_mask); |
| debug_printf_exec("running applet '%s'\n", argv[0]); | |
| run_applet_no_and_exit(a, argv[0], argv); | |
| } | |
| @@ -8045,6 +8047,24 @@ static NOINLINE int run_pipe(struct pipe *pi) | |
| add_vars(old_vars); | |
| /* clean_up_and_ret0: */ | |
| restore_redirects(squirrel); | |
| + | /* |
| + | * Try "usleep 99999999" + ^C + "echo $?" |
| + | * with FEATURE_SH_NOFORK=y. |
| + | */ |
| + | if (!funcp) { |
| + | /* It was builtin or nofork. |
| + | * if this would be a real fork/execed program, |
| + | * it should have died if a fatal sig was received. |
| + | * But OTOH, there was no separate process, |
| + | * the sig was sent to _shell_, not to non-existing |
| + | * child. |
| + | * Let's just handle ^C only, this one is obvious: |
| + | * we aren't ok with exitcode 0 when ^C was pressed |
| + | * during builtin/nofork. |
| + | */ |
| + | if (sigismember(&G.pending_set, SIGINT)) |
| + | rcode = 128 + SIGINT; |
| + | } |
| clean_up_and_ret1: | |
| free(argv_expanded); | |
| IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) | |
| @@ -8060,6 +8080,14 @@ static NOINLINE int run_pipe(struct pipe *pi) | |
| if (rcode == 0) { | |
| debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", | |
| argv_expanded[0], argv_expanded[1]); | |
| + | /* |
| + | * Note: signals (^C) can't interrupt here. |
| + | * We remember them and they will be acted upon |
| + | * after applet returns. |
| + | * This makes applets which can run for a long time |
| + | * and/or wait for user input ineligible for NOFORK: |
| + | * for example, "yes" or "rm" (rm -i waits for input). |
| + | */ |
| rcode = run_nofork_applet(n, argv_expanded); | |
| } | |
| goto clean_up_and_ret; | |
| @@ -8491,7 +8519,7 @@ static int run_list(struct pipe *pi) | |
| G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; | |
| G.last_bg_pid_exitcode = 0; | |
| debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); | |
| - | /* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */ |
| + | /* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash say 0 */ |
| rcode = EXIT_SUCCESS; | |
| goto check_traps; | |
| } else { | |
| @@ -10178,6 +10206,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid | |
| /* So, did we get a signal? */ | |
| sig = check_and_run_traps(); | |
| if (sig /*&& sig != SIGCHLD - always true */) { | |
| + | /* Do this for any (non-ignored) signal, not only for ^C */ |
| ret = 128 + sig; | |
| break; | |
| } | |