hush: add more complex case to leak testcase, fix found breakage

function                                             old     new   delta
unset_local_var_len                                    -     167    +167
run_list                                            2350    2457    +107
set_vars_and_save_old                                  -      87     +87
free_pipe                                            207     227     +20
builtin_unset                                        220     229      +9
builtin_exit                                          49      47      -2
free_strings_and_unset                                53       -     -53
set_vars_all_and_save_old                             87       -     -87
unset_local_var                                      168       -    -168
------------------------------------------------------------------------------
(add/remove: 2/3 grow/shrink: 3/1 up/down: 390/-310)           Total: 80 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2009-05-04 01:58:10 +02:00
parent cb6ff25afe
commit acdc49c073
2 changed files with 47 additions and 38 deletions

View File

@ -904,30 +904,20 @@ static char **xx_add_string_to_strings(int lineno, char **strings, char *add)
xx_add_string_to_strings(__LINE__, strings, add) xx_add_string_to_strings(__LINE__, strings, add)
#endif #endif
static int unset_local_var(const char *name); static void free_strings(char **strings)
static void free_strings_and_unset(char **strings, int unset)
{ {
char **v; char **v;
if (!strings) if (!strings)
return; return;
v = strings; v = strings;
while (*v) { while (*v) {
if (unset) { free(*v);
unset_local_var(*v); v++;
}
free(*v++);
} }
free(strings); free(strings);
} }
static void free_strings(char **strings)
{
free_strings_and_unset(strings, 0);
}
/* Helpers for setting new $n and restoring them back /* Helpers for setting new $n and restoring them back
*/ */
@ -1340,25 +1330,21 @@ static int set_local_var(char *str, int flg_export, int flg_read_only)
return 0; return 0;
} }
static int unset_local_var(const char *name) static int unset_local_var_len(const char *name, int name_len)
{ {
struct variable *cur; struct variable *cur;
struct variable *prev = prev; /* for gcc */ struct variable **var_pp;
int name_len;
if (!name) if (!name)
return EXIT_SUCCESS; return EXIT_SUCCESS;
name_len = strlen(name); var_pp = &G.top_var;
cur = G.top_var; while ((cur = *var_pp) != NULL) {
while (cur) {
if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
if (cur->flg_read_only) { if (cur->flg_read_only) {
bb_error_msg("%s: readonly variable", name); bb_error_msg("%s: readonly variable", name);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
/* prev is ok to use here because 1st variable, HUSH_VERSION, *var_pp = cur->next;
* is ro, and we cannot reach this code on the 1st pass */
prev->next = cur->next;
debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr); debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr);
bb_unsetenv(cur->varstr); bb_unsetenv(cur->varstr);
if (name_len == 3 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') if (name_len == 3 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
@ -1368,12 +1354,31 @@ static int unset_local_var(const char *name)
free(cur); free(cur);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
prev = cur; var_pp = &cur->next;
cur = cur->next;
} }
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
static int unset_local_var(const char *name)
{
return unset_local_var_len(name, strlen(name));
}
static void unset_vars(char **strings)
{
char **v;
if (!strings)
return;
v = strings;
while (*v) {
const char *eq = strchrnul(*v, '=');
unset_local_var_len(*v, (int)(eq - *v));
v++;
}
free(strings);
}
#if ENABLE_SH_MATH_SUPPORT #if ENABLE_SH_MATH_SUPPORT
#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
@ -1411,13 +1416,17 @@ static void add_vars(struct variable *var)
next = var->next; next = var->next;
var->next = G.top_var; var->next = G.top_var;
G.top_var = var; G.top_var = var;
if (var->flg_export) if (var->flg_export) {
debug_printf_env("%s: restoring exported '%s'\n", __func__, var->varstr);
putenv(var->varstr); putenv(var->varstr);
} else {
debug_printf_env("%s: restoring local '%s'\n", __func__, var->varstr);
}
var = next; var = next;
} }
} }
static struct variable *set_vars_all_and_save_old(char **strings) static struct variable *set_vars_and_save_old(char **strings)
{ {
char **s; char **s;
struct variable *old = NULL; struct variable *old = NULL;
@ -1438,6 +1447,7 @@ static struct variable *set_vars_all_and_save_old(char **strings)
if (var_pp) { if (var_pp) {
/* Remove variable from global linked list */ /* Remove variable from global linked list */
var_p = *var_pp; var_p = *var_pp;
debug_printf_env("%s: removing '%s'\n", __func__, var_p->varstr);
*var_pp = var_p->next; *var_pp = var_p->next;
/* Add it to returned list */ /* Add it to returned list */
var_p->next = old; var_p->next = old;
@ -3021,13 +3031,13 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save,
new_env = expand_assignments(argv, assignment_cnt); new_env = expand_assignments(argv, assignment_cnt);
#if BB_MMU #if BB_MMU
set_vars_all_and_save_old(new_env); set_vars_and_save_old(new_env);
free(new_env); /* optional */ free(new_env); /* optional */
/* we can also destroy set_vars_all_and_save_old's return value, /* we can also destroy set_vars_and_save_old's return value,
* to save memory */ * to save memory */
#else #else
nommu_save->new_env = new_env; nommu_save->new_env = new_env;
nommu_save->old_vars = set_vars_all_and_save_old(new_env); nommu_save->old_vars = set_vars_and_save_old(new_env);
#endif #endif
if (argv_expanded) { if (argv_expanded) {
argv = argv_expanded; argv = argv_expanded;
@ -3554,7 +3564,7 @@ static int run_pipe(struct pipe *pi)
rcode = setup_redirects(command, squirrel); rcode = setup_redirects(command, squirrel);
if (rcode == 0) { if (rcode == 0) {
new_env = expand_assignments(argv, command->assignment_cnt); new_env = expand_assignments(argv, command->assignment_cnt);
old_vars = set_vars_all_and_save_old(new_env); old_vars = set_vars_and_save_old(new_env);
if (!funcp) { if (!funcp) {
debug_printf_exec(": builtin '%s' '%s'...\n", debug_printf_exec(": builtin '%s' '%s'...\n",
x->cmd, argv_expanded[1]); x->cmd, argv_expanded[1]);
@ -3573,7 +3583,7 @@ static int run_pipe(struct pipe *pi)
clean_up_and_ret: clean_up_and_ret:
#endif #endif
restore_redirects(squirrel); restore_redirects(squirrel);
free_strings_and_unset(new_env, 1); unset_vars(new_env);
add_vars(old_vars); add_vars(old_vars);
clean_up_and_ret1: clean_up_and_ret1:
free(argv_expanded); free(argv_expanded);
@ -3589,7 +3599,7 @@ static int run_pipe(struct pipe *pi)
rcode = setup_redirects(command, squirrel); rcode = setup_redirects(command, squirrel);
if (rcode == 0) { if (rcode == 0) {
new_env = expand_assignments(argv, command->assignment_cnt); new_env = expand_assignments(argv, command->assignment_cnt);
old_vars = set_vars_all_and_save_old(new_env); old_vars = set_vars_and_save_old(new_env);
debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
argv_expanded[0], argv_expanded[1]); argv_expanded[0], argv_expanded[1]);
rcode = run_nofork_applet(i, argv_expanded); rcode = run_nofork_applet(i, argv_expanded);
@ -3687,7 +3697,7 @@ static int run_pipe(struct pipe *pi)
/* Clean up after vforked child */ /* Clean up after vforked child */
free(nommu_save.argv); free(nommu_save.argv);
free(nommu_save.argv_from_re_execing); free(nommu_save.argv_from_re_execing);
free_strings_and_unset(nommu_save.new_env, 1); unset_vars(nommu_save.new_env);
add_vars(nommu_save.old_vars); add_vars(nommu_save.old_vars);
#endif #endif
free(argv_expanded); free(argv_expanded);

View File

@ -64,7 +64,7 @@ HERE
trap "echo trap$i" WINCH trap "echo trap$i" WINCH
f() { true; true; true; true; true; true; true; true; } f() { true; true; true; true; true; true; true; true; }
f() { true; true; true; true; true; true; true; true; echo $1; } f() { true; true; true; true; true; true; true; true; echo $1; }
f >/dev/null i=iii$i t=ttt$i z=zzz$i f >/dev/null
: $((i++)) : $((i++))
done done
unset i l t unset i l t
@ -132,7 +132,7 @@ HERE
trap "echo trap$i" WINCH trap "echo trap$i" WINCH
f() { true; true; true; true; true; true; true; true; } f() { true; true; true; true; true; true; true; true; }
f() { true; true; true; true; true; true; true; true; echo $1; } f() { true; true; true; true; true; true; true; true; echo $1; }
f >/dev/null i=iii$i t=ttt$i z=zzz$i f >/dev/null
: $((i++)) : $((i++))
done done
unset i l t unset i l t
@ -141,9 +141,8 @@ unset -f f
memleak memleak
kb=$? kb=$?
# Observed some variability, bumped to 12k if test $kb -le 4; then
if test $kb -le 12; then
echo Ok #$kb echo Ok #$kb
else else
echo "Bad: $kb kb (or more) leaked" echo "Bad: $kb kb leaked"
fi fi