diff options
-rw-r--r-- | shell/hush.c | 76 | ||||
-rwxr-xr-x | shell/hush_test/hush-z_slow/leak_all1.tests | 9 |
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 | ||
907 | static int unset_local_var(const char *name); | 907 | static void free_strings(char **strings) |
908 | |||
909 | static 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 | ||
926 | static 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 | ||
1343 | static int unset_local_var(const char *name) | 1333 | static 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 | ||
1362 | static int unset_local_var(const char *name) | ||
1363 | { | ||
1364 | return unset_local_var_len(name, strlen(name)); | ||
1365 | } | ||
1366 | |||
1367 | static 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 | ||
1420 | static struct variable *set_vars_all_and_save_old(char **strings) | 1429 | static 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++)) |
69 | done | 69 | done |
70 | unset i l t | 70 | unset 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++)) |
137 | done | 137 | done |
138 | unset i l t | 138 | unset i l t |
@@ -141,9 +141,8 @@ unset -f f | |||
141 | 141 | ||
142 | memleak | 142 | memleak |
143 | kb=$? | 143 | kb=$? |
144 | # Observed some variability, bumped to 12k | 144 | if test $kb -le 4; then |
145 | if test $kb -le 12; then | ||
146 | echo Ok #$kb | 145 | echo Ok #$kb |
147 | else | 146 | else |
148 | echo "Bad: $kb kb (or more) leaked" | 147 | echo "Bad: $kb kb leaked" |
149 | fi | 148 | fi |