aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/hush.c76
-rwxr-xr-xshell/hush_test/hush-z_slow/leak_all1.tests9
2 files changed, 47 insertions, 38 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 0890f0977..d1f674e9d 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -904,30 +904,20 @@ static char **xx_add_string_to_strings(int lineno, char **strings, char *add)
904 xx_add_string_to_strings(__LINE__, strings, add) 904 xx_add_string_to_strings(__LINE__, strings, add)
905#endif 905#endif
906 906
907static int unset_local_var(const char *name); 907static void free_strings(char **strings)
908
909static void free_strings_and_unset(char **strings, int unset)
910{ 908{
911 char **v; 909 char **v;
912 910
913 if (!strings) 911 if (!strings)
914 return; 912 return;
915
916 v = strings; 913 v = strings;
917 while (*v) { 914 while (*v) {
918 if (unset) { 915 free(*v);
919 unset_local_var(*v); 916 v++;
920 }
921 free(*v++);
922 } 917 }
923 free(strings); 918 free(strings);
924} 919}
925 920
926static void free_strings(char **strings)
927{
928 free_strings_and_unset(strings, 0);
929}
930
931 921
932/* Helpers for setting new $n and restoring them back 922/* Helpers for setting new $n and restoring them back
933 */ 923 */
@@ -1340,25 +1330,21 @@ static int set_local_var(char *str, int flg_export, int flg_read_only)
1340 return 0; 1330 return 0;
1341} 1331}
1342 1332
1343static int unset_local_var(const char *name) 1333static int unset_local_var_len(const char *name, int name_len)
1344{ 1334{
1345 struct variable *cur; 1335 struct variable *cur;
1346 struct variable *prev = prev; /* for gcc */ 1336 struct variable **var_pp;
1347 int name_len;
1348 1337
1349 if (!name) 1338 if (!name)
1350 return EXIT_SUCCESS; 1339 return EXIT_SUCCESS;
1351 name_len = strlen(name); 1340 var_pp = &G.top_var;
1352 cur = G.top_var; 1341 while ((cur = *var_pp) != NULL) {
1353 while (cur) {
1354 if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { 1342 if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
1355 if (cur->flg_read_only) { 1343 if (cur->flg_read_only) {
1356 bb_error_msg("%s: readonly variable", name); 1344 bb_error_msg("%s: readonly variable", name);
1357 return EXIT_FAILURE; 1345 return EXIT_FAILURE;
1358 } 1346 }
1359 /* prev is ok to use here because 1st variable, HUSH_VERSION, 1347 *var_pp = cur->next;
1360 * is ro, and we cannot reach this code on the 1st pass */
1361 prev->next = cur->next;
1362 debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr); 1348 debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr);
1363 bb_unsetenv(cur->varstr); 1349 bb_unsetenv(cur->varstr);
1364 if (name_len == 3 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') 1350 if (name_len == 3 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
@@ -1368,12 +1354,31 @@ static int unset_local_var(const char *name)
1368 free(cur); 1354 free(cur);
1369 return EXIT_SUCCESS; 1355 return EXIT_SUCCESS;
1370 } 1356 }
1371 prev = cur; 1357 var_pp = &cur->next;
1372 cur = cur->next;
1373 } 1358 }
1374 return EXIT_SUCCESS; 1359 return EXIT_SUCCESS;
1375} 1360}
1376 1361
1362static int unset_local_var(const char *name)
1363{
1364 return unset_local_var_len(name, strlen(name));
1365}
1366
1367static void unset_vars(char **strings)
1368{
1369 char **v;
1370
1371 if (!strings)
1372 return;
1373 v = strings;
1374 while (*v) {
1375 const char *eq = strchrnul(*v, '=');
1376 unset_local_var_len(*v, (int)(eq - *v));
1377 v++;
1378 }
1379 free(strings);
1380}
1381
1377#if ENABLE_SH_MATH_SUPPORT 1382#if ENABLE_SH_MATH_SUPPORT
1378#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) 1383#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1379#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) 1384#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
@@ -1411,13 +1416,17 @@ static void add_vars(struct variable *var)
1411 next = var->next; 1416 next = var->next;
1412 var->next = G.top_var; 1417 var->next = G.top_var;
1413 G.top_var = var; 1418 G.top_var = var;
1414 if (var->flg_export) 1419 if (var->flg_export) {
1420 debug_printf_env("%s: restoring exported '%s'\n", __func__, var->varstr);
1415 putenv(var->varstr); 1421 putenv(var->varstr);
1422 } else {
1423 debug_printf_env("%s: restoring local '%s'\n", __func__, var->varstr);
1424 }
1416 var = next; 1425 var = next;
1417 } 1426 }
1418} 1427}
1419 1428
1420static struct variable *set_vars_all_and_save_old(char **strings) 1429static struct variable *set_vars_and_save_old(char **strings)
1421{ 1430{
1422 char **s; 1431 char **s;
1423 struct variable *old = NULL; 1432 struct variable *old = NULL;
@@ -1438,6 +1447,7 @@ static struct variable *set_vars_all_and_save_old(char **strings)
1438 if (var_pp) { 1447 if (var_pp) {
1439 /* Remove variable from global linked list */ 1448 /* Remove variable from global linked list */
1440 var_p = *var_pp; 1449 var_p = *var_pp;
1450 debug_printf_env("%s: removing '%s'\n", __func__, var_p->varstr);
1441 *var_pp = var_p->next; 1451 *var_pp = var_p->next;
1442 /* Add it to returned list */ 1452 /* Add it to returned list */
1443 var_p->next = old; 1453 var_p->next = old;
@@ -3021,13 +3031,13 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save,
3021 3031
3022 new_env = expand_assignments(argv, assignment_cnt); 3032 new_env = expand_assignments(argv, assignment_cnt);
3023#if BB_MMU 3033#if BB_MMU
3024 set_vars_all_and_save_old(new_env); 3034 set_vars_and_save_old(new_env);
3025 free(new_env); /* optional */ 3035 free(new_env); /* optional */
3026 /* we can also destroy set_vars_all_and_save_old's return value, 3036 /* we can also destroy set_vars_and_save_old's return value,
3027 * to save memory */ 3037 * to save memory */
3028#else 3038#else
3029 nommu_save->new_env = new_env; 3039 nommu_save->new_env = new_env;
3030 nommu_save->old_vars = set_vars_all_and_save_old(new_env); 3040 nommu_save->old_vars = set_vars_and_save_old(new_env);
3031#endif 3041#endif
3032 if (argv_expanded) { 3042 if (argv_expanded) {
3033 argv = argv_expanded; 3043 argv = argv_expanded;
@@ -3554,7 +3564,7 @@ static int run_pipe(struct pipe *pi)
3554 rcode = setup_redirects(command, squirrel); 3564 rcode = setup_redirects(command, squirrel);
3555 if (rcode == 0) { 3565 if (rcode == 0) {
3556 new_env = expand_assignments(argv, command->assignment_cnt); 3566 new_env = expand_assignments(argv, command->assignment_cnt);
3557 old_vars = set_vars_all_and_save_old(new_env); 3567 old_vars = set_vars_and_save_old(new_env);
3558 if (!funcp) { 3568 if (!funcp) {
3559 debug_printf_exec(": builtin '%s' '%s'...\n", 3569 debug_printf_exec(": builtin '%s' '%s'...\n",
3560 x->cmd, argv_expanded[1]); 3570 x->cmd, argv_expanded[1]);
@@ -3573,7 +3583,7 @@ static int run_pipe(struct pipe *pi)
3573 clean_up_and_ret: 3583 clean_up_and_ret:
3574#endif 3584#endif
3575 restore_redirects(squirrel); 3585 restore_redirects(squirrel);
3576 free_strings_and_unset(new_env, 1); 3586 unset_vars(new_env);
3577 add_vars(old_vars); 3587 add_vars(old_vars);
3578 clean_up_and_ret1: 3588 clean_up_and_ret1:
3579 free(argv_expanded); 3589 free(argv_expanded);
@@ -3589,7 +3599,7 @@ static int run_pipe(struct pipe *pi)
3589 rcode = setup_redirects(command, squirrel); 3599 rcode = setup_redirects(command, squirrel);
3590 if (rcode == 0) { 3600 if (rcode == 0) {
3591 new_env = expand_assignments(argv, command->assignment_cnt); 3601 new_env = expand_assignments(argv, command->assignment_cnt);
3592 old_vars = set_vars_all_and_save_old(new_env); 3602 old_vars = set_vars_and_save_old(new_env);
3593 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", 3603 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
3594 argv_expanded[0], argv_expanded[1]); 3604 argv_expanded[0], argv_expanded[1]);
3595 rcode = run_nofork_applet(i, argv_expanded); 3605 rcode = run_nofork_applet(i, argv_expanded);
@@ -3687,7 +3697,7 @@ static int run_pipe(struct pipe *pi)
3687 /* Clean up after vforked child */ 3697 /* Clean up after vforked child */
3688 free(nommu_save.argv); 3698 free(nommu_save.argv);
3689 free(nommu_save.argv_from_re_execing); 3699 free(nommu_save.argv_from_re_execing);
3690 free_strings_and_unset(nommu_save.new_env, 1); 3700 unset_vars(nommu_save.new_env);
3691 add_vars(nommu_save.old_vars); 3701 add_vars(nommu_save.old_vars);
3692#endif 3702#endif
3693 free(argv_expanded); 3703 free(argv_expanded);
diff --git a/shell/hush_test/hush-z_slow/leak_all1.tests b/shell/hush_test/hush-z_slow/leak_all1.tests
index 4e3c4fdec..f8207cf04 100755
--- a/shell/hush_test/hush-z_slow/leak_all1.tests
+++ b/shell/hush_test/hush-z_slow/leak_all1.tests
@@ -64,7 +64,7 @@ HERE
64 trap "echo trap$i" WINCH 64 trap "echo trap$i" WINCH
65 f() { true; true; true; true; true; true; true; true; } 65 f() { true; true; true; true; true; true; true; true; }
66 f() { true; true; true; true; true; true; true; true; echo $1; } 66 f() { true; true; true; true; true; true; true; true; echo $1; }
67 f >/dev/null 67 i=iii$i t=ttt$i z=zzz$i f >/dev/null
68 : $((i++)) 68 : $((i++))
69done 69done
70unset i l t 70unset i l t
@@ -132,7 +132,7 @@ HERE
132 trap "echo trap$i" WINCH 132 trap "echo trap$i" WINCH
133 f() { true; true; true; true; true; true; true; true; } 133 f() { true; true; true; true; true; true; true; true; }
134 f() { true; true; true; true; true; true; true; true; echo $1; } 134 f() { true; true; true; true; true; true; true; true; echo $1; }
135 f >/dev/null 135 i=iii$i t=ttt$i z=zzz$i f >/dev/null
136 : $((i++)) 136 : $((i++))
137done 137done
138unset i l t 138unset i l t
@@ -141,9 +141,8 @@ unset -f f
141 141
142memleak 142memleak
143kb=$? 143kb=$?
144# Observed some variability, bumped to 12k 144if test $kb -le 4; then
145if test $kb -le 12; then
146 echo Ok #$kb 145 echo Ok #$kb
147else 146else
148 echo "Bad: $kb kb (or more) leaked" 147 echo "Bad: $kb kb leaked"
149fi 148fi