From e5814a5a42f40274aaadc1187680ffe5c4632c0e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 16 Jul 2016 18:33:55 +0200 Subject: ash: do not leave SIGQUIT ignored on "exec CMD" Signed-off-by: Denys Vlasenko --- shell/ash.c | 15 ++++++++++++++- shell/ash_test/ash-signals/sigquit_exec.right | 2 ++ shell/ash_test/ash-signals/sigquit_exec.tests | 4 ++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 shell/ash_test/ash-signals/sigquit_exec.right create mode 100755 shell/ash_test/ash-signals/sigquit_exec.tests (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index faa45a8dc..4f6376f78 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -323,7 +323,7 @@ struct globals_misc { #define S_DFL 1 /* default signal handling (SIG_DFL) */ #define S_CATCH 2 /* signal is caught */ #define S_IGN 3 /* signal is ignored (SIG_IGN) */ -#define S_HARD_IGN 4 /* signal is ignored permenantly */ +#define S_HARD_IGN 4 /* signal is ignored permanently */ /* indicates specified signal received */ uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ @@ -9024,7 +9024,20 @@ execcmd(int argc UNUSED_PARAM, char **argv) iflag = 0; /* exit on error */ mflag = 0; optschanged(); + /* We should set up signals for "exec CMD" + * the same way as for "CMD" without "exec". + * But optschanged->setinteractive->setsignal + * still thought we are a root shell. Therefore, for example, + * SIGQUIT is still set to IGN. Fix it: + */ + shlvl++; + setsignal(SIGQUIT); + /*setsignal(SIGTERM); - unnecessary because of iflag=0 */ + /*setsignal(SIGTSTP); - unnecessary because of mflag=0 */ + /*setsignal(SIGTTOU); - unnecessary because of mflag=0 */ + shellexec(argv + 1, pathval(), 0); + /* NOTREACHED */ } return 0; } diff --git a/shell/ash_test/ash-signals/sigquit_exec.right b/shell/ash_test/ash-signals/sigquit_exec.right new file mode 100644 index 000000000..a8041929a --- /dev/null +++ b/shell/ash_test/ash-signals/sigquit_exec.right @@ -0,0 +1,2 @@ +SigIgn: 0000000000000000 +SigIgn: 0000000000000000 diff --git a/shell/ash_test/ash-signals/sigquit_exec.tests b/shell/ash_test/ash-signals/sigquit_exec.tests new file mode 100755 index 000000000..24bda6921 --- /dev/null +++ b/shell/ash_test/ash-signals/sigquit_exec.tests @@ -0,0 +1,4 @@ +# Should show no masked signals in both cases. +# We had a bug where SIGQUIT was masked on exec. +grep SigIgn: /proc/self/status +exec grep SigIgn: /proc/self/status -- cgit v1.2.3-55-g6feb From 49117b48008e0fe36d6680c787045cb44a300f93 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 21 Jul 2016 14:40:08 +0200 Subject: hush: fix a possible bug Not sure this was actually a triggerable bug, but the code looked flaky. Signed-off-by: Denys Vlasenko --- shell/hush.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index eabe83ac6..ab192e2cd 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -9153,9 +9153,11 @@ static int FAST_FUNC builtin_break(char **argv) unsigned depth; if (G.depth_of_loop == 0) { bb_error_msg("%s: only meaningful in a loop", argv[0]); + /* if we came from builtin_continue(), need to undo "= 1" */ + G.flag_break_continue = 0; return EXIT_SUCCESS; /* bash compat */ } - G.flag_break_continue++; /* BC_BREAK = 1 */ + G.flag_break_continue++; /* BC_BREAK = 1, or BC_CONTINUE = 2 */ G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1); if (depth == UINT_MAX) -- cgit v1.2.3-55-g6feb From 0fb0045aa9261be1dda49dfdfb95cbc585402a8b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 22 Jul 2016 18:48:38 +0200 Subject: config: disentangle PREFER_APPLETS from SH_STANDALONE and SH_NOFORK On user request. I thought enabling/disabling them all together is more consistent. Evidently, some people do want them to be separately selectable. Signed-off-by: Denys Vlasenko --- applets/applet_tables.c | 4 +++- include/busybox.h | 4 +++- libbb/vfork_daemon_rexec.c | 5 +++-- shell/Config.src | 4 ++-- 4 files changed, 11 insertions(+), 6 deletions(-) (limited to 'shell') diff --git a/applets/applet_tables.c b/applets/applet_tables.c index 843f2ec08..8401a1549 100644 --- a/applets/applet_tables.c +++ b/applets/applet_tables.c @@ -143,7 +143,9 @@ int main(int argc, char **argv) printf("};\n"); printf("#endif\n\n"); -#if ENABLE_FEATURE_PREFER_APPLETS +#if ENABLE_FEATURE_PREFER_APPLETS \ + || ENABLE_FEATURE_SH_STANDALONE \ + || ENABLE_FEATURE_SH_NOFORK printf("const uint8_t applet_flags[] ALIGN1 = {\n"); i = 0; while (i < NUM_APPLETS) { diff --git a/include/busybox.h b/include/busybox.h index 737627bd0..6a003d544 100644 --- a/include/busybox.h +++ b/include/busybox.h @@ -19,7 +19,9 @@ extern const uint8_t applet_flags[] ALIGN1; extern const uint8_t applet_suid[] ALIGN1; extern const uint8_t applet_install_loc[] ALIGN1; -#if ENABLE_FEATURE_PREFER_APPLETS +#if ENABLE_FEATURE_PREFER_APPLETS \ + || ENABLE_FEATURE_SH_STANDALONE \ + || ENABLE_FEATURE_SH_NOFORK # define APPLET_IS_NOFORK(i) (applet_flags[(i)/4] & (1 << (2 * ((i)%4)))) # define APPLET_IS_NOEXEC(i) (applet_flags[(i)/4] & (1 << ((2 * ((i)%4))+1))) #else diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c index 1adb5b3c4..c192829b5 100644 --- a/libbb/vfork_daemon_rexec.c +++ b/libbb/vfork_daemon_rexec.c @@ -68,7 +68,8 @@ pid_t FAST_FUNC xspawn(char **argv) return pid; } -#if ENABLE_FEATURE_PREFER_APPLETS +#if ENABLE_FEATURE_PREFER_APPLETS \ + || ENABLE_FEATURE_SH_NOFORK static jmp_buf die_jmp; static void jump(void) { @@ -174,7 +175,7 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv) return rc & 0xff; /* don't confuse people with "exitcodes" >255 */ } -#endif /* FEATURE_PREFER_APPLETS */ +#endif /* FEATURE_PREFER_APPLETS || FEATURE_SH_NOFORK */ int FAST_FUNC spawn_and_wait(char **argv) { diff --git a/shell/Config.src b/shell/Config.src index b31e62dda..e4df35973 100644 --- a/shell/Config.src +++ b/shell/Config.src @@ -88,7 +88,7 @@ config FEATURE_SH_EXTRA_QUIET config FEATURE_SH_STANDALONE bool "Standalone shell" default n - depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS + depends on (HUSH || ASH) help This option causes busybox shells to use busybox applets in preference to executables in the PATH whenever possible. For @@ -121,7 +121,7 @@ config FEATURE_SH_STANDALONE config FEATURE_SH_NOFORK bool "Run 'nofork' applets directly" default n - depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS + depends on (HUSH || ASH) help This option causes busybox shells to not execute typical fork/exec/wait sequence, but call _main directly, -- cgit v1.2.3-55-g6feb From f8ddbe1ccce9eceaaac28b4b1aa71631fcc56db6 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 25 Jul 2016 03:56:00 +0200 Subject: ash: fix handling of ${VAR: -2} Signed-off-by: Denys Vlasenko --- shell/ash.c | 12 ++++++++---- shell/ash_test/ash-vars/var_bash1a.right | 6 ++++++ shell/ash_test/ash-vars/var_bash1a.tests | 11 +++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 shell/ash_test/ash-vars/var_bash1a.right create mode 100755 shell/ash_test/ash-vars/var_bash1a.tests (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 4f6376f78..496167fbe 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6323,6 +6323,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype, #if ENABLE_ASH_BASH_COMPAT case VSSUBSTR: +//TODO: support more general format ${v:EXPR:EXPR}, +// where EXPR follows $(()) rules loc = str = stackblock() + strloc; /* Read POS in ${var:POS:LEN} */ pos = atoi(loc); /* number(loc) errors out on "1:4" */ @@ -11577,15 +11579,18 @@ parsesub: { STPUTC('=', out); flags = 0; if (subtype == 0) { + static const char types[] ALIGN1 = "}-+?="; /* ${VAR...} but not $VAR or ${#VAR} */ /* c == first char after VAR */ switch (c) { case ':': c = pgetc(); #if ENABLE_ASH_BASH_COMPAT - if (c == ':' || c == '$' || isdigit(c)) { -//TODO: support more general format ${v:EXPR:EXPR}, -// where EXPR follows $(()) rules + /* This check is only needed to not misinterpret + * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD} + * constructs. + */ + if (!strchr(types, c)) { subtype = VSSUBSTR; pungetc(); break; /* "goto do_pungetc" is bigger (!) */ @@ -11594,7 +11599,6 @@ parsesub: { flags = VSNUL; /*FALLTHROUGH*/ default: { - static const char types[] ALIGN1 = "}-+?="; const char *p = strchr(types, c); if (p == NULL) goto badsub; diff --git a/shell/ash_test/ash-vars/var_bash1a.right b/shell/ash_test/ash-vars/var_bash1a.right new file mode 100644 index 000000000..1965b5c6c --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash1a.right @@ -0,0 +1,6 @@ +parameter 'abcdef' +varoffset2 'cdef' +varoffset-2 'ef' +literal '2' 'cdef' +literal '-2' 'abcdef' +literal ' -2' 'ef' diff --git a/shell/ash_test/ash-vars/var_bash1a.tests b/shell/ash_test/ash-vars/var_bash1a.tests new file mode 100755 index 000000000..551dd9acc --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash1a.tests @@ -0,0 +1,11 @@ +parameter=abcdef +offset=2 +noffset=-2 +echo "parameter '${parameter}'" +echo "varoffset2 '${parameter:${offset}}'" +echo "varoffset-2 '${parameter:${noffset}}'" +echo "literal '2' '${parameter:2}'" +# This is not inrpreted as ${VAR:POS{:LEN}}, +# but as ${VAR:=WORD} - if VAR is unset or null, substitute WORD +echo "literal '-2' '${parameter:-2}'" +echo "literal ' -2' '${parameter: -2}'" -- cgit v1.2.3-55-g6feb From 215b0ca6e4fe466c6942d21a1bba62d97f2d5e5d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 19 Aug 2016 18:23:56 +0200 Subject: hush: fix a bug in FEATURE_SH_STANDALONE=y config. Closes 9186 Run this in a "sh SCRIPT": sha256sum /dev/null echo END sha256sum is a NOEXEC applet. It runs in a forked child. Then child exit()s. By this time, entire script is read, and buffered in a FILE object from fopen("SCRIPT"). But fgetc() did not consume entire input. exit() lseeks back by -9 bytes, from to 'e' in 'echo'. (this may be libc-specific). This change of fd position *is shared with the parent*! Now parent can read more, and it thinks there is another "echo END". End result: two "echo END"s are run. Fix this by _exit()ing instead. Signed-off-by: Denys Vlasenko --- libbb/appletlib.c | 4 +++- shell/hush.c | 23 ++++++++++++++++++++--- shell/hush_test/run-all | 3 ++- 3 files changed, 25 insertions(+), 5 deletions(-) (limited to 'shell') diff --git a/libbb/appletlib.c b/libbb/appletlib.c index c341817e2..f760af2cb 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -877,7 +877,9 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) } if (ENABLE_FEATURE_SUID) check_suid(applet_no); - exit(applet_main[applet_no](argc, argv)); + xfunc_error_retval = applet_main[applet_no](argc, argv); + /* Note: applet_main() may also not return (die on a xfunc or such) */ + xfunc_die(); } # endif /* NUM_APPLETS > 0 */ diff --git a/shell/hush.c b/shell/hush.c index ab192e2cd..be5c98a20 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -1580,11 +1580,11 @@ static void hush_exit(int exitcode) } #endif -#if ENABLE_HUSH_JOB fflush_all(); +#if ENABLE_HUSH_JOB sigexit(- (exitcode & 0xff)); #else - exit(exitcode); + _exit(exitcode); #endif } @@ -6466,7 +6466,23 @@ static void dump_cmd_in_x_mode(char **argv) * Never returns. * Don't exit() here. If you don't exec, use _exit instead. * The at_exit handlers apparently confuse the calling process, - * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ + * in particular stdin handling. Not sure why? -- because of vfork! (vda) + * Also, it was observed that on exit(), fgetc'ed buffered data + * gets "unwound" by some libcs, via lseek(fd, -NUM, SEEK_CUR). + * With the net effect that even after fork(), not vfork(), + * exit() in NOEXECed applet in "sh SCRIPT": + * noexec_applet_here + * echo END_OF_SCRIPT + * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT". + * This makes "echo END_OF_SCRIPT" executed twice. exexit() is the fix. + */ +#if ENABLE_FEATURE_SH_STANDALONE +static void exexit(void) +{ + fflush_all(); + _exit(xfunc_error_retval); +} +#endif static void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignment_cnt, char **argv_expanded) NORETURN; @@ -6547,6 +6563,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, # if BB_MMU /* see above why on NOMMU it is not allowed */ if (APPLET_IS_NOEXEC(a)) { debug_printf_exec("running applet '%s'\n", argv[0]); + die_func = exexit; run_applet_no_and_exit(a, argv); } # endif diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all index 64a7abc47..837b3f7da 100755 --- a/shell/hush_test/run-all +++ b/shell/hush_test/run-all @@ -64,11 +64,12 @@ do_test() echo -n "$1/$x:" ( "$THIS_SH" "./$x" >"$name.xx" 2>&1 + r=$? # filter C library differences sed -i \ -e "/: invalid option /s:'::g" \ "$name.xx" - test $? -eq 77 && rm -f "../$1-$x.fail" && exit 77 + test $r -eq 77 && rm -f "../$1-$x.fail" && exit 77 diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" ) case $? in -- cgit v1.2.3-55-g6feb From e9abe75fda82a986b0b40969ebdd4aa92bcb52e3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 19 Aug 2016 20:15:26 +0200 Subject: hush: `cmd` and arithmetic also need the fix for FILE rewind Discovered by running testsuite with a newest glibc Signed-off-by: Denys Vlasenko --- shell/hush.c | 71 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 32 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index be5c98a20..b41d9d04b 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -1477,19 +1477,50 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler) return old_sa.sa_handler; } +static void hush_exit(int exitcode) NORETURN; +static void fflush_and__exit(void) NORETURN; +static void restore_ttypgrp_and__exit(void) NORETURN; + +static void restore_ttypgrp_and__exit(void) +{ + /* xfunc has failed! die die die */ + /* no EXIT traps, this is an escape hatch! */ + G.exiting = 1; + hush_exit(xfunc_error_retval); +} + +/* Needed only on some libc: + * It was observed that on exit(), fgetc'ed buffered data + * gets "unwound" via lseek(fd, -NUM, SEEK_CUR). + * With the net effect that even after fork(), not vfork(), + * exit() in NOEXECed applet in "sh SCRIPT": + * noexec_applet_here + * echo END_OF_SCRIPT + * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT". + * This makes "echo END_OF_SCRIPT" executed twice. + * Similar problems can be seen with die_if_script() -> xfunc_die() + * and in `cmd` handling. + * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): + */ +static void fflush_and__exit(void) +{ + fflush_all(); + _exit(xfunc_error_retval); +} + #if ENABLE_HUSH_JOB -static void xfunc_has_died(void); /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ -# define disable_restore_tty_pgrp_on_exit() (die_func = NULL) +# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit) /* After [v]fork, in parent: restore tty pgrp on xfunc death */ -# define enable_restore_tty_pgrp_on_exit() (die_func = xfunc_has_died) +# define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit) /* Restores tty foreground process group, and exits. * May be called as signal handler for fatal signal * (will resend signal to itself, producing correct exit state) * or called directly with -EXITCODE. - * We also call it if xfunc is exiting. */ + * We also call it if xfunc is exiting. + */ static void sigexit(int sig) NORETURN; static void sigexit(int sig) { @@ -1544,7 +1575,6 @@ static sighandler_t pick_sighandler(unsigned sig) } /* Restores tty foreground process group, and exits. */ -static void hush_exit(int exitcode) NORETURN; static void hush_exit(int exitcode) { #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT @@ -1588,15 +1618,6 @@ static void hush_exit(int exitcode) #endif } -static void xfunc_has_died(void) NORETURN; -static void xfunc_has_died(void) -{ - /* xfunc has failed! die die die */ - /* no EXIT traps, this is an escape hatch! */ - G.exiting = 1; - hush_exit(xfunc_error_retval); -} - //TODO: return a mask of ALL handled sigs? static int check_and_run_traps(void) @@ -5913,7 +5934,8 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) ) { static const char *const argv[] = { NULL, NULL }; builtin_trap((char**)argv); - exit(0); /* not _exit() - we need to fflush */ + fflush_all(); /* important */ + _exit(0); } # if BB_MMU reset_traps_to_defaults(); @@ -6467,22 +6489,7 @@ static void dump_cmd_in_x_mode(char **argv) * Don't exit() here. If you don't exec, use _exit instead. * The at_exit handlers apparently confuse the calling process, * in particular stdin handling. Not sure why? -- because of vfork! (vda) - * Also, it was observed that on exit(), fgetc'ed buffered data - * gets "unwound" by some libcs, via lseek(fd, -NUM, SEEK_CUR). - * With the net effect that even after fork(), not vfork(), - * exit() in NOEXECed applet in "sh SCRIPT": - * noexec_applet_here - * echo END_OF_SCRIPT - * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT". - * This makes "echo END_OF_SCRIPT" executed twice. exexit() is the fix. */ -#if ENABLE_FEATURE_SH_STANDALONE -static void exexit(void) -{ - fflush_all(); - _exit(xfunc_error_retval); -} -#endif static void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignment_cnt, char **argv_expanded) NORETURN; @@ -6563,7 +6570,6 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, # if BB_MMU /* see above why on NOMMU it is not allowed */ if (APPLET_IS_NOEXEC(a)) { debug_printf_exec("running applet '%s'\n", argv[0]); - die_func = exexit; run_applet_no_and_exit(a, argv); } # endif @@ -7804,6 +7810,7 @@ int hush_main(int argc, char **argv) INIT_G(); if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ G.last_exitcode = EXIT_SUCCESS; + #if ENABLE_HUSH_FAST G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ #endif @@ -7893,7 +7900,7 @@ int hush_main(int argc, char **argv) /* Initialize some more globals to non-zero values */ cmdedit_update_prompt(); - die_func = xfunc_has_died; + die_func = restore_ttypgrp_and__exit; /* Shell is non-interactive at first. We need to call * install_special_sighandlers() if we are going to execute "sh