aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-11 10:37:10 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-11 10:37:10 +0000
commited055214bba86644e1c2168b0e1d1bd7fa82a93c (patch)
treea22b8eb401d5d6ed57cf7e8bfff1adb95af67f23
parent75bccfa375564337bfbd57e5d54f92e155a0b18b (diff)
downloadbusybox-w32-ed055214bba86644e1c2168b0e1d1bd7fa82a93c.tar.gz
busybox-w32-ed055214bba86644e1c2168b0e1d1bd7fa82a93c.tar.bz2
busybox-w32-ed055214bba86644e1c2168b0e1d1bd7fa82a93c.zip
hush: fix "while...do f1() {a;}; f1; f1 {b;}; f1; done" bug
-rw-r--r--shell/hush.c74
-rwxr-xr-xshell/hush_test/hush-z_slow/leak_all1.tests6
2 files changed, 59 insertions, 21 deletions
diff --git a/shell/hush.c b/shell/hush.c
index a055ec14f..a56b2e6ca 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -330,7 +330,7 @@ struct redir_struct {
330 /* note: for heredocs, rd_filename contains heredoc delimiter, 330 /* note: for heredocs, rd_filename contains heredoc delimiter,
331 * and subsequently heredoc itself; and rd_dup is a bitmask: 331 * and subsequently heredoc itself; and rd_dup is a bitmask:
332 * 1: do we need to trim leading tabs? 332 * 1: do we need to trim leading tabs?
333 * 2: is heredoc quoted (<<'dleim' syntax) ? 333 * 2: is heredoc quoted (<<'delim' syntax) ?
334 */ 334 */
335}; 335};
336typedef enum redir_type { 336typedef enum redir_type {
@@ -357,25 +357,42 @@ struct command {
357 int assignment_cnt; /* how many argv[i] are assignments? */ 357 int assignment_cnt; /* how many argv[i] are assignments? */
358 smallint is_stopped; /* is the command currently running? */ 358 smallint is_stopped; /* is the command currently running? */
359 smallint grp_type; /* GRP_xxx */ 359 smallint grp_type; /* GRP_xxx */
360#define GRP_NORMAL 0
361#define GRP_SUBSHELL 1
362#if ENABLE_HUSH_FUNCTIONS
363# define GRP_FUNCTION 2
364#endif
360 struct pipe *group; /* if non-NULL, this "command" is { list }, 365 struct pipe *group; /* if non-NULL, this "command" is { list },
361 * ( list ), or a compound statement */ 366 * ( list ), or a compound statement */
362#if !BB_MMU 367#if !BB_MMU
363 char *group_as_string; 368 char *group_as_string;
364#endif 369#endif
370#if ENABLE_HUSH_FUNCTIONS
371 struct function *child_func;
372/* This field is used to prevent a bug here:
373 * while...do f1() {a;}; f1; f1 {b;}; f1; done
374 * When we execute "f1() {a;}" cmd, we create new function and clear
375 * cmd->group, cmd->group_as_string, cmd->argv[0].
376 * when we execute "f1 {b;}", we notice that f1 exists,
377 * and that it's "parent cmd" struct is still "alive",
378 * we put those fields back into cmd->xxx
379 * (struct function has ->parent_cmd ptr to facilitate that).
380 * When we loop back, we can execute "f1() {a;}" again and set f1 correctly.
381 * Without this trick, loop would execute a;b;b;b;...
382 * instead of correct sequence a;b;a;b;...
383 * When command is freed, it severs the link
384 * (sets ->child_func->parent_cmd to NULL).
385 */
386#endif
365 char **argv; /* command name and arguments */ 387 char **argv; /* command name and arguments */
366 struct redir_struct *redirects; /* I/O redirections */
367};
368/* argv vector may contain variable references (^Cvar^C, ^C0^C etc) 388/* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
369 * and on execution these are substituted with their values. 389 * and on execution these are substituted with their values.
370 * Substitution can make _several_ words out of one argv[n]! 390 * Substitution can make _several_ words out of one argv[n]!
371 * Example: argv[0]=='.^C*^C.' here: echo .$*. 391 * Example: argv[0]=='.^C*^C.' here: echo .$*.
372 * References of the form ^C`cmd arg^C are `cmd arg` substitutions. 392 * References of the form ^C`cmd arg^C are `cmd arg` substitutions.
373 */ 393 */
374#define GRP_NORMAL 0 394 struct redir_struct *redirects; /* I/O redirections */
375#define GRP_SUBSHELL 1 395};
376#if ENABLE_HUSH_FUNCTIONS
377# define GRP_FUNCTION 2
378#endif
379 396
380struct pipe { 397struct pipe {
381 struct pipe *next; 398 struct pipe *next;
@@ -456,6 +473,7 @@ enum {
456struct function { 473struct function {
457 struct function *next; 474 struct function *next;
458 char *name; 475 char *name;
476 struct command *parent_cmd;
459 struct pipe *body; 477 struct pipe *body;
460#if !BB_MMU 478#if !BB_MMU
461 char *body_as_string; 479 char *body_as_string;
@@ -743,7 +761,6 @@ static void syntax_error_unexpected_ch(unsigned lineno, char ch)
743 msg[0] = ch; 761 msg[0] = ch;
744 msg[1] = '\0'; 762 msg[1] = '\0';
745 die_if_script(lineno, "syntax error: unexpected %s", msg); 763 die_if_script(lineno, "syntax error: unexpected %s", msg);
746 xfunc_die();
747} 764}
748 765
749#if HUSH_DEBUG < 2 766#if HUSH_DEBUG < 2
@@ -2573,6 +2590,14 @@ static void free_pipe(struct pipe *pi, int indent)
2573 debug_printf_clean("%s end group\n", indenter(indent)); 2590 debug_printf_clean("%s end group\n", indenter(indent));
2574 command->group = NULL; 2591 command->group = NULL;
2575 } 2592 }
2593 /* else is crucial here.
2594 * If group != NULL, child_func is meaningless */
2595#if ENABLE_HUSH_FUNCTIONS
2596 else if (command->child_func) {
2597 debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
2598 command->child_func->parent_cmd = NULL;
2599 }
2600#endif
2576#if !BB_MMU 2601#if !BB_MMU
2577 free(command->group_as_string); 2602 free(command->group_as_string);
2578 command->group_as_string = NULL; 2603 command->group_as_string = NULL;
@@ -3173,9 +3198,24 @@ static int run_pipe(struct pipe *pi)
3173 3198
3174 while ((funcp = *funcpp) != NULL) { 3199 while ((funcp = *funcpp) != NULL) {
3175 if (strcmp(funcp->name, command->argv[0]) == 0) { 3200 if (strcmp(funcp->name, command->argv[0]) == 0) {
3176 debug_printf_exec("replacing function '%s'\n", funcp->name); 3201 struct command *cmd = funcp->parent_cmd;
3177 free(funcp->name); 3202
3178 free_pipe_list(funcp->body, /* indent: */ 0); 3203 debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
3204 if (!cmd) {
3205 debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
3206 free(funcp->name);
3207 free_pipe_list(funcp->body, /* indent: */ 0);
3208#if !BB_MMU
3209 free(funcp->body_as_string);
3210#endif
3211 } else {
3212 debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
3213 cmd->argv[0] = funcp->name;
3214 cmd->group = funcp->body;
3215#if !BB_MMU
3216 cmd->group_as_string = funcp->body_as_string;
3217#endif
3218 }
3179 goto skip; 3219 goto skip;
3180 } 3220 }
3181 funcpp = &funcp->next; 3221 funcpp = &funcp->next;
@@ -3192,13 +3232,9 @@ static int run_pipe(struct pipe *pi)
3192#endif 3232#endif
3193 command->group = NULL; 3233 command->group = NULL;
3194 command->argv[0] = NULL; 3234 command->argv[0] = NULL;
3195 free_strings(command->argv); 3235 debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
3196 command->argv = NULL; 3236 funcp->parent_cmd = command;
3197 /* note: if we are in a loop, future "executions" 3237 command->child_func = funcp;
3198 * of func def will see it as null command since
3199 * command->group == NULL and command->argv == NULL */
3200//this isn't exactly right: while...do f1() {a;}; f1; f1 {b;}; done
3201//second loop will execute b!
3202 3238
3203 return EXIT_SUCCESS; 3239 return EXIT_SUCCESS;
3204 } 3240 }
diff --git a/shell/hush_test/hush-z_slow/leak_all1.tests b/shell/hush_test/hush-z_slow/leak_all1.tests
index 4c9d41afb..21fdb0d1e 100755
--- a/shell/hush_test/hush-z_slow/leak_all1.tests
+++ b/shell/hush_test/hush-z_slow/leak_all1.tests
@@ -62,7 +62,8 @@ HERE
62 echo >/dev/null ${var%%*} 62 echo >/dev/null ${var%%*}
63 set -- par1_$i par2_$i par3_$i par4_$i 63 set -- par1_$i par2_$i par3_$i par4_$i
64 trap "echo trap$i" WINCH 64 trap "echo trap$i" WINCH
65 f() { echo $1; } 65 f() { true; true; true; true; true; true; true; true; }
66 f() { true; true; true; true; true; true; true; true; echo $1; }
66 f >/dev/null 67 f >/dev/null
67 : $((i++)) 68 : $((i++))
68done 69done
@@ -127,7 +128,8 @@ HERE
127 echo >/dev/null ${var%%*} 128 echo >/dev/null ${var%%*}
128 set -- par1_$i par2_$i par3_$i par4_$i 129 set -- par1_$i par2_$i par3_$i par4_$i
129 trap "echo trap$i" WINCH 130 trap "echo trap$i" WINCH
130 f() { echo $1; } 131 f() { true; true; true; true; true; true; true; true; }
132 f() { true; true; true; true; true; true; true; true; echo $1; }
131 f >/dev/null 133 f >/dev/null
132 : $((i++)) 134 : $((i++))
133done 135done