diff options
author | Denys Vlasenko | 2018-04-05 14:41:21 +0200 |
---|---|---|
committer | Denys Vlasenko | 2018-04-05 14:41:21 +0200 |
commit | 41d8f1081378ec79586d59e7d2a31380b6f95577 (patch) | |
tree | 947c94f2d458ee834a8bb31986241af958cae853 /shell/hush.c | |
parent | 929a41d5770c0531f037c2e7db25bf98f9029c9e (diff) | |
download | busybox-41d8f1081378ec79586d59e7d2a31380b6f95577.zip busybox-41d8f1081378ec79586d59e7d2a31380b6f95577.tar.gz |
hush: fix corner cases with exec in empty expansions
Cases like these:
var=val exec >redir
var=val `` >redir
function old new delta
run_pipe 1701 1723 +22
redirect_and_varexp_helper 56 55 -1
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 22/-1) Total: 21 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 43 |
1 files changed, 26 insertions, 17 deletions
diff --git a/shell/hush.c b/shell/hush.c index 24ae237..4370236 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -8200,19 +8200,21 @@ static int redirect_and_varexp_helper( struct squirrel **sqp, char **argv_expanded) { + /* Assignments occur before redirects. Try: + * a=`sleep 1` sleep 2 3>/qwe/rty + */ + + char **new_env = expand_assignments(command->argv, command->assignment_cnt); + dump_cmd_in_x_mode(new_env); + dump_cmd_in_x_mode(argv_expanded); + /* this takes ownership of new_env[i] elements, and frees new_env: */ + set_vars_and_save_old(new_env); + /* setup_redirects acts on file descriptors, not FILEs. * This is perfect for work that comes after exec(). * Is it really safe for inline use? Experimentally, * things seem to work. */ - int rcode = setup_redirects(command, sqp); - if (rcode == 0) { - char **new_env = expand_assignments(command->argv, command->assignment_cnt); - dump_cmd_in_x_mode(new_env); - dump_cmd_in_x_mode(argv_expanded); - /* this takes ownership of new_env[i] elements, and frees new_env: */ - set_vars_and_save_old(new_env); - } - return rcode; + return setup_redirects(command, sqp); } static NOINLINE int run_pipe(struct pipe *pi) { @@ -8315,6 +8317,7 @@ static NOINLINE int run_pipe(struct pipe *pi) * Ensure redirects take effect (that is, create files). * Try "a=t >file" */ + only_assignments: G.expand_exitcode = 0; rcode = setup_redirects(command, &squirrel); @@ -8359,12 +8362,10 @@ static NOINLINE int run_pipe(struct pipe *pi) #endif argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); - /* if someone gives us an empty string: `cmd with empty output` */ -//TODO: what about: var=EXPR `` >FILE ? will var be set? Will FILE be created? + /* If someone gives us an empty string: `cmd with empty output` */ if (!argv_expanded[0]) { free(argv_expanded); - debug_leave(); - return G.last_exitcode; + goto only_assignments; } old_vars = NULL; @@ -8378,9 +8379,17 @@ static NOINLINE int run_pipe(struct pipe *pi) if (x || funcp) { if (x && x->b_function == builtin_exec && argv_expanded[1] == NULL) { debug_printf("exec with redirects only\n"); - rcode = setup_redirects(command, NULL); -//TODO: what about: var=EXPR exec >FILE ? will var be set? + /* + * Variable assignments are executed, but then "forgotten": + * a=`sleep 1;echo A` exec 3>&-; echo $a + * sleeps, but prints nothing. + */ + enter_var_nest_level(); + G.shadowed_vars_pp = &old_vars; + rcode = redirect_and_varexp_helper(command, /*squirrel:*/ NULL, argv_expanded); + G.shadowed_vars_pp = sv_shadowed; /* rcode=1 can be if redir file can't be opened */ + goto clean_up_and_ret1; } @@ -8452,9 +8461,10 @@ static NOINLINE int run_pipe(struct pipe *pi) } else goto must_fork; + restore_redirects(squirrel); + clean_up_and_ret1: leave_var_nest_level(); add_vars(old_vars); - restore_redirects(squirrel); /* * Try "usleep 99999999" + ^C + "echo $?" @@ -8474,7 +8484,6 @@ static NOINLINE int run_pipe(struct pipe *pi) 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;) debug_leave(); |