diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2018-04-04 22:32:59 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2018-04-04 22:35:13 +0200 |
| commit | 332e4115c978094b3630cde62a7154a4fcd564d8 (patch) | |
| tree | 6b07958a7e9a430d6475b0dc21e08c343e162ecf /shell | |
| parent | 61407807ab6b9505041a74ad96ffb4aeab937d36 (diff) | |
| download | busybox-w32-332e4115c978094b3630cde62a7154a4fcd564d8.tar.gz busybox-w32-332e4115c978094b3630cde62a7154a4fcd564d8.tar.bz2 busybox-w32-332e4115c978094b3630cde62a7154a4fcd564d8.zip | |
hush: make var nesting code independent of "local" support
Also, add code to abort at ~65000 function recursion depth.
SEGVing is not as nice as exiting with a message (and restoring termios!):
$ f() { echo -n .; f; }; f
....<many dots later>....hush: fatal recursion (depth 65281)
function old new delta
run_pipe 1826 1890 +64
pseudo_exec_argv 544 554 +10
parse_and_run_file 71 80 +9
i_getch 104 107 +3
done_command 99 102 +3
set_local_var 508 510 +2
helper_export_local 214 215 +1
builtin_local 49 46 -3
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 7/1 up/down: 92/-3) Total: 89 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 101 |
1 files changed, 61 insertions, 40 deletions
diff --git a/shell/hush.c b/shell/hush.c index 641531209..9fb9b5f32 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -730,10 +730,8 @@ struct parse_context { | |||
| 730 | struct variable { | 730 | struct variable { |
| 731 | struct variable *next; | 731 | struct variable *next; |
| 732 | char *varstr; /* points to "name=" portion */ | 732 | char *varstr; /* points to "name=" portion */ |
| 733 | #if ENABLE_HUSH_LOCAL | ||
| 734 | unsigned func_nest_level; | ||
| 735 | #endif | ||
| 736 | int max_len; /* if > 0, name is part of initial env; else name is malloced */ | 733 | int max_len; /* if > 0, name is part of initial env; else name is malloced */ |
| 734 | uint16_t var_nest_level; | ||
| 737 | smallint flg_export; /* putenv should be done on this var */ | 735 | smallint flg_export; /* putenv should be done on this var */ |
| 738 | smallint flg_read_only; | 736 | smallint flg_read_only; |
| 739 | }; | 737 | }; |
| @@ -922,12 +920,13 @@ struct globals { | |||
| 922 | const char *cwd; | 920 | const char *cwd; |
| 923 | struct variable *top_var; | 921 | struct variable *top_var; |
| 924 | char **expanded_assignments; | 922 | char **expanded_assignments; |
| 923 | struct variable **shadowed_vars_pp; | ||
| 924 | unsigned var_nest_level; | ||
| 925 | #if ENABLE_HUSH_FUNCTIONS | 925 | #if ENABLE_HUSH_FUNCTIONS |
| 926 | struct function *top_func; | ||
| 927 | # if ENABLE_HUSH_LOCAL | 926 | # if ENABLE_HUSH_LOCAL |
| 928 | struct variable **shadowed_vars_pp; | 927 | unsigned func_nest_level; /* solely to prevent "local v" in non-functions */ |
| 929 | unsigned func_nest_level; | ||
| 930 | # endif | 928 | # endif |
| 929 | struct function *top_func; | ||
| 931 | #endif | 930 | #endif |
| 932 | /* Signal and trap handling */ | 931 | /* Signal and trap handling */ |
| 933 | #if ENABLE_HUSH_FAST | 932 | #if ENABLE_HUSH_FAST |
| @@ -2131,7 +2130,7 @@ static const char* FAST_FUNC get_local_var_value(const char *name) | |||
| 2131 | #define SETFLAG_EXPORT (1 << 0) | 2130 | #define SETFLAG_EXPORT (1 << 0) |
| 2132 | #define SETFLAG_UNEXPORT (1 << 1) | 2131 | #define SETFLAG_UNEXPORT (1 << 1) |
| 2133 | #define SETFLAG_MAKE_RO (1 << 2) | 2132 | #define SETFLAG_MAKE_RO (1 << 2) |
| 2134 | #define SETFLAG_LOCAL_SHIFT 3 | 2133 | #define SETFLAG_VARLVL_SHIFT 3 |
| 2135 | static int set_local_var(char *str, unsigned flags) | 2134 | static int set_local_var(char *str, unsigned flags) |
| 2136 | { | 2135 | { |
| 2137 | struct variable **cur_pp; | 2136 | struct variable **cur_pp; |
| @@ -2139,7 +2138,7 @@ static int set_local_var(char *str, unsigned flags) | |||
| 2139 | char *free_me = NULL; | 2138 | char *free_me = NULL; |
| 2140 | char *eq_sign; | 2139 | char *eq_sign; |
| 2141 | int name_len; | 2140 | int name_len; |
| 2142 | IF_HUSH_LOCAL(unsigned local_lvl = (flags >> SETFLAG_LOCAL_SHIFT);) | 2141 | unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT); |
| 2143 | 2142 | ||
| 2144 | eq_sign = strchr(str, '='); | 2143 | eq_sign = strchr(str, '='); |
| 2145 | if (!eq_sign) { /* not expected to ever happen? */ | 2144 | if (!eq_sign) { /* not expected to ever happen? */ |
| @@ -2176,8 +2175,7 @@ static int set_local_var(char *str, unsigned flags) | |||
| 2176 | unsetenv(str); | 2175 | unsetenv(str); |
| 2177 | *eq_sign = '='; | 2176 | *eq_sign = '='; |
| 2178 | } | 2177 | } |
| 2179 | #if ENABLE_HUSH_LOCAL | 2178 | if (cur->var_nest_level < local_lvl) { |
| 2180 | if (cur->func_nest_level < local_lvl) { | ||
| 2181 | /* New variable is declared as local, | 2179 | /* New variable is declared as local, |
| 2182 | * and existing one is global, or local | 2180 | * and existing one is global, or local |
| 2183 | * from enclosing function. | 2181 | * from enclosing function. |
| @@ -2197,7 +2195,7 @@ static int set_local_var(char *str, unsigned flags) | |||
| 2197 | flags |= SETFLAG_EXPORT; | 2195 | flags |= SETFLAG_EXPORT; |
| 2198 | break; | 2196 | break; |
| 2199 | } | 2197 | } |
| 2200 | #endif | 2198 | |
| 2201 | if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) { | 2199 | if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) { |
| 2202 | free_and_exp: | 2200 | free_and_exp: |
| 2203 | free(str); | 2201 | free(str); |
| @@ -2225,7 +2223,7 @@ static int set_local_var(char *str, unsigned flags) | |||
| 2225 | 2223 | ||
| 2226 | /* Not found - create new variable struct */ | 2224 | /* Not found - create new variable struct */ |
| 2227 | cur = xzalloc(sizeof(*cur)); | 2225 | cur = xzalloc(sizeof(*cur)); |
| 2228 | IF_HUSH_LOCAL(cur->func_nest_level = local_lvl;) | 2226 | cur->var_nest_level = local_lvl; |
| 2229 | cur->next = *cur_pp; | 2227 | cur->next = *cur_pp; |
| 2230 | *cur_pp = cur; | 2228 | *cur_pp = cur; |
| 2231 | 2229 | ||
| @@ -5509,7 +5507,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5509 | goto parse_error2; | 5507 | goto parse_error2; |
| 5510 | default: | 5508 | default: |
| 5511 | if (HUSH_DEBUG) | 5509 | if (HUSH_DEBUG) |
| 5512 | bb_error_msg_and_die("BUG: unexpected %c\n", ch); | 5510 | bb_error_msg_and_die("BUG: unexpected %c", ch); |
| 5513 | } | 5511 | } |
| 5514 | } /* while (1) */ | 5512 | } /* while (1) */ |
| 5515 | 5513 | ||
| @@ -7362,8 +7360,9 @@ static void exec_function(char ***to_free, | |||
| 7362 | // for "more correctness" we might want to close those extra fds here: | 7360 | // for "more correctness" we might want to close those extra fds here: |
| 7363 | //? close_saved_fds_and_FILE_fds(); | 7361 | //? close_saved_fds_and_FILE_fds(); |
| 7364 | 7362 | ||
| 7365 | /* "we are in function, ok to use return" */ | 7363 | /* "we are in a function, ok to use return" */ |
| 7366 | G_flag_return_in_progress = -1; | 7364 | G_flag_return_in_progress = -1; |
| 7365 | G.var_nest_level++; | ||
| 7367 | IF_HUSH_LOCAL(G.func_nest_level++;) | 7366 | IF_HUSH_LOCAL(G.func_nest_level++;) |
| 7368 | 7367 | ||
| 7369 | /* On MMU, funcp->body is always non-NULL */ | 7368 | /* On MMU, funcp->body is always non-NULL */ |
| @@ -7383,6 +7382,47 @@ static void exec_function(char ***to_free, | |||
| 7383 | # endif | 7382 | # endif |
| 7384 | } | 7383 | } |
| 7385 | 7384 | ||
| 7385 | static void enter_var_nest_level(void) | ||
| 7386 | { | ||
| 7387 | G.var_nest_level++; | ||
| 7388 | |||
| 7389 | /* Try: f() { echo -n .; f; }; f | ||
| 7390 | * struct variable::var_nest_level is uint16_t, | ||
| 7391 | * thus limiting recursion to < 2^16. | ||
| 7392 | * In any case, with 8 Mbyte stack SEGV happens | ||
| 7393 | * not too long after 2^16 recursions anyway. | ||
| 7394 | */ | ||
| 7395 | if (G.var_nest_level > 0xff00) | ||
| 7396 | bb_error_msg_and_die("fatal recursion (depth %u)", G.var_nest_level); | ||
| 7397 | } | ||
| 7398 | |||
| 7399 | static void leave_var_nest_level(void) | ||
| 7400 | { | ||
| 7401 | struct variable *cur; | ||
| 7402 | struct variable **cur_pp; | ||
| 7403 | |||
| 7404 | cur_pp = &G.top_var; | ||
| 7405 | while ((cur = *cur_pp) != NULL) { | ||
| 7406 | if (cur->var_nest_level < G.var_nest_level) { | ||
| 7407 | cur_pp = &cur->next; | ||
| 7408 | continue; | ||
| 7409 | } | ||
| 7410 | /* Unexport */ | ||
| 7411 | if (cur->flg_export) | ||
| 7412 | bb_unsetenv(cur->varstr); | ||
| 7413 | /* Remove from global list */ | ||
| 7414 | *cur_pp = cur->next; | ||
| 7415 | /* Free */ | ||
| 7416 | if (!cur->max_len) | ||
| 7417 | free(cur->varstr); | ||
| 7418 | free(cur); | ||
| 7419 | } | ||
| 7420 | |||
| 7421 | G.var_nest_level--; | ||
| 7422 | if (HUSH_DEBUG && (int)G.var_nest_level < 0) | ||
| 7423 | bb_error_msg_and_die("BUG: nesting underflow"); | ||
| 7424 | } | ||
| 7425 | |||
| 7386 | static int run_function(const struct function *funcp, char **argv) | 7426 | static int run_function(const struct function *funcp, char **argv) |
| 7387 | { | 7427 | { |
| 7388 | int rc; | 7428 | int rc; |
| @@ -7394,6 +7434,8 @@ static int run_function(const struct function *funcp, char **argv) | |||
| 7394 | /* "we are in function, ok to use return" */ | 7434 | /* "we are in function, ok to use return" */ |
| 7395 | sv_flg = G_flag_return_in_progress; | 7435 | sv_flg = G_flag_return_in_progress; |
| 7396 | G_flag_return_in_progress = -1; | 7436 | G_flag_return_in_progress = -1; |
| 7437 | |||
| 7438 | enter_var_nest_level(); | ||
| 7397 | IF_HUSH_LOCAL(G.func_nest_level++;) | 7439 | IF_HUSH_LOCAL(G.func_nest_level++;) |
| 7398 | 7440 | ||
| 7399 | /* On MMU, funcp->body is always non-NULL */ | 7441 | /* On MMU, funcp->body is always non-NULL */ |
| @@ -7408,30 +7450,9 @@ static int run_function(const struct function *funcp, char **argv) | |||
| 7408 | rc = run_list(funcp->body); | 7450 | rc = run_list(funcp->body); |
| 7409 | } | 7451 | } |
| 7410 | 7452 | ||
| 7411 | # if ENABLE_HUSH_LOCAL | 7453 | IF_HUSH_LOCAL(G.func_nest_level--;) |
| 7412 | { | 7454 | leave_var_nest_level(); |
| 7413 | struct variable *var; | ||
| 7414 | struct variable **var_pp; | ||
| 7415 | 7455 | ||
| 7416 | var_pp = &G.top_var; | ||
| 7417 | while ((var = *var_pp) != NULL) { | ||
| 7418 | if (var->func_nest_level < G.func_nest_level) { | ||
| 7419 | var_pp = &var->next; | ||
| 7420 | continue; | ||
| 7421 | } | ||
| 7422 | /* Unexport */ | ||
| 7423 | if (var->flg_export) | ||
| 7424 | bb_unsetenv(var->varstr); | ||
| 7425 | /* Remove from global list */ | ||
| 7426 | *var_pp = var->next; | ||
| 7427 | /* Free */ | ||
| 7428 | if (!var->max_len) | ||
| 7429 | free(var->varstr); | ||
| 7430 | free(var); | ||
| 7431 | } | ||
| 7432 | G.func_nest_level--; | ||
| 7433 | } | ||
| 7434 | # endif | ||
| 7435 | G_flag_return_in_progress = sv_flg; | 7456 | G_flag_return_in_progress = sv_flg; |
| 7436 | 7457 | ||
| 7437 | restore_G_args(&sv, argv); | 7458 | restore_G_args(&sv, argv); |
| @@ -9959,8 +9980,8 @@ static int helper_export_local(char **argv, unsigned flags) | |||
| 9959 | # if ENABLE_HUSH_LOCAL | 9980 | # if ENABLE_HUSH_LOCAL |
| 9960 | /* Is this "local" bltin? */ | 9981 | /* Is this "local" bltin? */ |
| 9961 | if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) { | 9982 | if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) { |
| 9962 | unsigned lvl = flags >> SETFLAG_LOCAL_SHIFT; | 9983 | unsigned lvl = flags >> SETFLAG_VARLVL_SHIFT; |
| 9963 | if (var && var->func_nest_level == lvl) { | 9984 | if (var && var->var_nest_level == lvl) { |
| 9964 | /* "local x=abc; ...; local x" - ignore second local decl */ | 9985 | /* "local x=abc; ...; local x" - ignore second local decl */ |
| 9965 | continue; | 9986 | continue; |
| 9966 | } | 9987 | } |
| @@ -10045,7 +10066,7 @@ static int FAST_FUNC builtin_local(char **argv) | |||
| 10045 | return EXIT_FAILURE; /* bash compat */ | 10066 | return EXIT_FAILURE; /* bash compat */ |
| 10046 | } | 10067 | } |
| 10047 | argv++; | 10068 | argv++; |
| 10048 | return helper_export_local(argv, G.func_nest_level << SETFLAG_LOCAL_SHIFT); | 10069 | return helper_export_local(argv, G.var_nest_level << SETFLAG_VARLVL_SHIFT); |
| 10049 | } | 10070 | } |
| 10050 | #endif | 10071 | #endif |
| 10051 | 10072 | ||
