diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-11 10:37:10 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-11 10:37:10 +0000 |
commit | ed055214bba86644e1c2168b0e1d1bd7fa82a93c (patch) | |
tree | a22b8eb401d5d6ed57cf7e8bfff1adb95af67f23 | |
parent | 75bccfa375564337bfbd57e5d54f92e155a0b18b (diff) | |
download | busybox-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.c | 74 | ||||
-rwxr-xr-x | shell/hush_test/hush-z_slow/leak_all1.tests | 6 |
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 | }; |
336 | typedef enum redir_type { | 336 | typedef 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 | ||
380 | struct pipe { | 397 | struct pipe { |
381 | struct pipe *next; | 398 | struct pipe *next; |
@@ -456,6 +473,7 @@ enum { | |||
456 | struct function { | 473 | struct 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++)) |
68 | done | 69 | done |
@@ -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++)) |
133 | done | 135 | done |