aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2018-04-04 22:32:59 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2018-04-04 22:35:13 +0200
commit332e4115c978094b3630cde62a7154a4fcd564d8 (patch)
tree6b07958a7e9a430d6475b0dc21e08c343e162ecf /shell
parent61407807ab6b9505041a74ad96ffb4aeab937d36 (diff)
downloadbusybox-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.c101
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 {
730struct variable { 730struct 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
2135static int set_local_var(char *str, unsigned flags) 2134static 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
7385static 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
7399static 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
7386static int run_function(const struct function *funcp, char **argv) 7426static 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