mirror of
https://github.com/sheumann/hush.git
synced 2024-12-21 23:29:34 +00:00
hush: fix bug where in "var=val func" var's value is not visible in func
function old new delta unset_local_var - 168 +168 set_vars_all_and_save_old - 87 +87 get_ptr_to_local_var - 77 +77 free_strings_and_unset - 53 +53 builtin_export 266 274 +8 get_local_var_value 31 33 +2 putenv_all 27 - -27 free_strings_and_unsetenv 53 - -53 get_local_var 68 - -68 run_list 2475 2350 -125 builtin_unset 380 220 -160 ------------------------------------------------------------------------------ (add/remove: 4/3 grow/shrink: 2/2 up/down: 395/-433) Total: -38 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
e19e1935a3
commit
cb6ff25afe
199
shell/hush.c
199
shell/hush.c
@ -56,7 +56,7 @@
|
|||||||
*
|
*
|
||||||
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
|
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
|
||||||
*/
|
*/
|
||||||
#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
|
#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
|
||||||
#include <glob.h>
|
#include <glob.h>
|
||||||
/* #include <dmalloc.h> */
|
/* #include <dmalloc.h> */
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
@ -65,7 +65,7 @@
|
|||||||
#include "math.h"
|
#include "math.h"
|
||||||
#include "match.h"
|
#include "match.h"
|
||||||
#ifndef PIPE_BUF
|
#ifndef PIPE_BUF
|
||||||
# define PIPE_BUF 4096 /* amount of buffering in a pipe */
|
# define PIPE_BUF 4096 /* amount of buffering in a pipe */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -138,6 +138,8 @@
|
|||||||
|
|
||||||
#define SPECIAL_VAR_SYMBOL 3
|
#define SPECIAL_VAR_SYMBOL 3
|
||||||
|
|
||||||
|
struct variable;
|
||||||
|
|
||||||
static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER;
|
static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER;
|
||||||
|
|
||||||
/* This supports saving pointers malloced in vfork child,
|
/* This supports saving pointers malloced in vfork child,
|
||||||
@ -146,7 +148,7 @@ static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER;
|
|||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
typedef struct nommu_save_t {
|
typedef struct nommu_save_t {
|
||||||
char **new_env;
|
char **new_env;
|
||||||
char **old_env;
|
struct variable *old_vars;
|
||||||
char **argv;
|
char **argv;
|
||||||
char **argv_from_re_execing;
|
char **argv_from_re_execing;
|
||||||
} nommu_save_t;
|
} nommu_save_t;
|
||||||
@ -282,8 +284,8 @@ struct command {
|
|||||||
#if ENABLE_HUSH_FUNCTIONS
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
# define GRP_FUNCTION 2
|
# define GRP_FUNCTION 2
|
||||||
#endif
|
#endif
|
||||||
struct pipe *group; /* if non-NULL, this "command" is { list },
|
/* if non-NULL, this "command" is { list }, ( list ), or a compound statement */
|
||||||
* ( list ), or a compound statement */
|
struct pipe *group;
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
char *group_as_string;
|
char *group_as_string;
|
||||||
#endif
|
#endif
|
||||||
@ -883,6 +885,7 @@ static char **xx_add_strings_to_strings(int lineno, char **strings, char **add,
|
|||||||
xx_add_strings_to_strings(__LINE__, strings, add, need_to_dup)
|
xx_add_strings_to_strings(__LINE__, strings, add, need_to_dup)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Note: takes ownership of "add" ptr (it is not strdup'ed) */
|
||||||
static char **add_string_to_strings(char **strings, char *add)
|
static char **add_string_to_strings(char **strings, char *add)
|
||||||
{
|
{
|
||||||
char *v[2];
|
char *v[2];
|
||||||
@ -901,44 +904,9 @@ 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 void putenv_all(char **strings)
|
static int unset_local_var(const char *name);
|
||||||
{
|
|
||||||
if (!strings)
|
|
||||||
return;
|
|
||||||
while (*strings) {
|
|
||||||
debug_printf_env("putenv '%s'\n", *strings);
|
|
||||||
putenv(*strings++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static char **putenv_all_and_save_old(char **strings)
|
static void free_strings_and_unset(char **strings, int unset)
|
||||||
{
|
|
||||||
char **old = NULL;
|
|
||||||
char **s = strings;
|
|
||||||
|
|
||||||
if (!strings)
|
|
||||||
return old;
|
|
||||||
while (*strings) {
|
|
||||||
char *v, *eq;
|
|
||||||
|
|
||||||
eq = strchr(*strings, '=');
|
|
||||||
if (eq) {
|
|
||||||
*eq = '\0';
|
|
||||||
v = getenv(*strings);
|
|
||||||
*eq = '=';
|
|
||||||
if (v) {
|
|
||||||
/* v points to VAL in VAR=VAL, go back to VAR */
|
|
||||||
v -= (eq - *strings) + 1;
|
|
||||||
old = add_string_to_strings(old, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
strings++;
|
|
||||||
}
|
|
||||||
putenv_all(s);
|
|
||||||
return old;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_strings_and_unsetenv(char **strings, int unset)
|
|
||||||
{
|
{
|
||||||
char **v;
|
char **v;
|
||||||
|
|
||||||
@ -948,8 +916,7 @@ static void free_strings_and_unsetenv(char **strings, int unset)
|
|||||||
v = strings;
|
v = strings;
|
||||||
while (*v) {
|
while (*v) {
|
||||||
if (unset) {
|
if (unset) {
|
||||||
debug_printf_env("unsetenv '%s'\n", *v);
|
unset_local_var(*v);
|
||||||
bb_unsetenv(*v);
|
|
||||||
}
|
}
|
||||||
free(*v++);
|
free(*v++);
|
||||||
}
|
}
|
||||||
@ -958,7 +925,7 @@ static void free_strings_and_unsetenv(char **strings, int unset)
|
|||||||
|
|
||||||
static void free_strings(char **strings)
|
static void free_strings(char **strings)
|
||||||
{
|
{
|
||||||
free_strings_and_unsetenv(strings, 0);
|
free_strings_and_unset(strings, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1242,27 +1209,38 @@ static const char *set_cwd(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Get/check local shell variables */
|
/*
|
||||||
static struct variable *get_local_var(const char *name)
|
* Shell and environment variable support
|
||||||
|
*/
|
||||||
|
static struct variable **get_ptr_to_local_var(const char *name)
|
||||||
{
|
{
|
||||||
|
struct variable **pp;
|
||||||
struct variable *cur;
|
struct variable *cur;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
if (!name)
|
|
||||||
return NULL;
|
|
||||||
len = strlen(name);
|
len = strlen(name);
|
||||||
for (cur = G.top_var; cur; cur = cur->next) {
|
pp = &G.top_var;
|
||||||
|
while ((cur = *pp) != NULL) {
|
||||||
if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
|
if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
|
||||||
return cur;
|
return pp;
|
||||||
|
pp = &cur->next;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *get_local_var_value(const char *src)
|
static struct variable *get_local_var(const char *name)
|
||||||
{
|
{
|
||||||
struct variable *var = get_local_var(src);
|
struct variable **pp = get_ptr_to_local_var(name);
|
||||||
if (var)
|
if (pp)
|
||||||
return strchr(var->varstr, '=') + 1;
|
return *pp;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_local_var_value(const char *name)
|
||||||
|
{
|
||||||
|
struct variable **pp = get_ptr_to_local_var(name);
|
||||||
|
if (pp)
|
||||||
|
return strchr((*pp)->varstr, '=') + 1;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1422,6 +1400,57 @@ static void arith_set_local_var(const char *name, const char *val, int flags)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helpers for "var1=val1 var2=val2 cmd" feature
|
||||||
|
*/
|
||||||
|
static void add_vars(struct variable *var)
|
||||||
|
{
|
||||||
|
struct variable *next;
|
||||||
|
|
||||||
|
while (var) {
|
||||||
|
next = var->next;
|
||||||
|
var->next = G.top_var;
|
||||||
|
G.top_var = var;
|
||||||
|
if (var->flg_export)
|
||||||
|
putenv(var->varstr);
|
||||||
|
var = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct variable *set_vars_all_and_save_old(char **strings)
|
||||||
|
{
|
||||||
|
char **s;
|
||||||
|
struct variable *old = NULL;
|
||||||
|
|
||||||
|
if (!strings)
|
||||||
|
return old;
|
||||||
|
s = strings;
|
||||||
|
while (*s) {
|
||||||
|
struct variable *var_p;
|
||||||
|
struct variable **var_pp;
|
||||||
|
char *eq;
|
||||||
|
|
||||||
|
eq = strchr(*s, '=');
|
||||||
|
if (eq) {
|
||||||
|
*eq = '\0';
|
||||||
|
var_pp = get_ptr_to_local_var(*s);
|
||||||
|
*eq = '=';
|
||||||
|
if (var_pp) {
|
||||||
|
/* Remove variable from global linked list */
|
||||||
|
var_p = *var_pp;
|
||||||
|
*var_pp = var_p->next;
|
||||||
|
/* Add it to returned list */
|
||||||
|
var_p->next = old;
|
||||||
|
old = var_p;
|
||||||
|
}
|
||||||
|
set_local_var(*s, 1, 0);
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* in_str support
|
* in_str support
|
||||||
*/
|
*/
|
||||||
@ -2855,17 +2884,17 @@ static struct function *new_function(char *name)
|
|||||||
* body_as_string was not malloced! */
|
* body_as_string was not malloced! */
|
||||||
if (funcp->body) {
|
if (funcp->body) {
|
||||||
free_pipe_list(funcp->body);
|
free_pipe_list(funcp->body);
|
||||||
#if !BB_MMU
|
# if !BB_MMU
|
||||||
free(funcp->body_as_string);
|
free(funcp->body_as_string);
|
||||||
#endif
|
# endif
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
|
debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
|
||||||
cmd->argv[0] = funcp->name;
|
cmd->argv[0] = funcp->name;
|
||||||
cmd->group = funcp->body;
|
cmd->group = funcp->body;
|
||||||
#if !BB_MMU
|
# if !BB_MMU
|
||||||
cmd->group_as_string = funcp->body_as_string;
|
cmd->group_as_string = funcp->body_as_string;
|
||||||
#endif
|
# endif
|
||||||
}
|
}
|
||||||
goto skip;
|
goto skip;
|
||||||
}
|
}
|
||||||
@ -2892,9 +2921,9 @@ static void unset_func(const char *name)
|
|||||||
* body_as_string was not malloced! */
|
* body_as_string was not malloced! */
|
||||||
if (funcp->body) {
|
if (funcp->body) {
|
||||||
free_pipe_list(funcp->body);
|
free_pipe_list(funcp->body);
|
||||||
#if !BB_MMU
|
# if !BB_MMU
|
||||||
free(funcp->body_as_string);
|
free(funcp->body_as_string);
|
||||||
#endif
|
# endif
|
||||||
}
|
}
|
||||||
free(funcp);
|
free(funcp);
|
||||||
break;
|
break;
|
||||||
@ -2903,10 +2932,10 @@ static void unset_func(const char *name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if BB_MMU
|
# if BB_MMU
|
||||||
#define exec_function(nommu_save, funcp, argv) \
|
#define exec_function(nommu_save, funcp, argv) \
|
||||||
exec_function(funcp, argv)
|
exec_function(funcp, argv)
|
||||||
#endif
|
# endif
|
||||||
static void exec_function(nommu_save_t *nommu_save,
|
static void exec_function(nommu_save_t *nommu_save,
|
||||||
const struct function *funcp,
|
const struct function *funcp,
|
||||||
char **argv) NORETURN;
|
char **argv) NORETURN;
|
||||||
@ -2938,37 +2967,31 @@ static int run_function(const struct function *funcp, char **argv)
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
save_arg_t sv;
|
save_arg_t sv;
|
||||||
#if ENABLE_HUSH_FUNCTIONS
|
|
||||||
smallint sv_flg;
|
smallint sv_flg;
|
||||||
#endif
|
|
||||||
|
|
||||||
save_and_replace_G_args(&sv, argv);
|
save_and_replace_G_args(&sv, argv);
|
||||||
#if ENABLE_HUSH_FUNCTIONS
|
|
||||||
/* "we are in function, ok to use return" */
|
/* "we are in function, ok to use return" */
|
||||||
sv_flg = G.flag_return_in_progress;
|
sv_flg = G.flag_return_in_progress;
|
||||||
G.flag_return_in_progress = -1;
|
G.flag_return_in_progress = -1;
|
||||||
#endif
|
|
||||||
|
|
||||||
/* On MMU, funcp->body is always non-NULL */
|
/* On MMU, funcp->body is always non-NULL */
|
||||||
#if !BB_MMU
|
# if !BB_MMU
|
||||||
if (!funcp->body) {
|
if (!funcp->body) {
|
||||||
/* Function defined by -F */
|
/* Function defined by -F */
|
||||||
parse_and_run_string(funcp->body_as_string);
|
parse_and_run_string(funcp->body_as_string);
|
||||||
rc = G.last_exitcode;
|
rc = G.last_exitcode;
|
||||||
} else
|
} else
|
||||||
#endif
|
# endif
|
||||||
{
|
{
|
||||||
rc = run_list(funcp->body);
|
rc = run_list(funcp->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_HUSH_FUNCTIONS
|
|
||||||
G.flag_return_in_progress = sv_flg;
|
G.flag_return_in_progress = sv_flg;
|
||||||
#endif
|
|
||||||
restore_G_args(&sv, argv);
|
restore_G_args(&sv, argv);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
#endif
|
#endif /* ENABLE_HUSH_FUNCTIONS */
|
||||||
|
|
||||||
|
|
||||||
#if BB_MMU
|
#if BB_MMU
|
||||||
@ -2998,11 +3021,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
|
||||||
putenv_all(new_env);
|
set_vars_all_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,
|
||||||
|
* to save memory */
|
||||||
#else
|
#else
|
||||||
nommu_save->new_env = new_env;
|
nommu_save->new_env = new_env;
|
||||||
nommu_save->old_env = putenv_all_and_save_old(new_env);
|
nommu_save->old_vars = set_vars_all_and_save_old(new_env);
|
||||||
#endif
|
#endif
|
||||||
if (argv_expanded) {
|
if (argv_expanded) {
|
||||||
argv = argv_expanded;
|
argv = argv_expanded;
|
||||||
@ -3443,10 +3468,10 @@ static int run_pipe(struct pipe *pi)
|
|||||||
funcp = new_function(command->argv[0]);
|
funcp = new_function(command->argv[0]);
|
||||||
/* funcp->name is already set to argv[0] */
|
/* funcp->name is already set to argv[0] */
|
||||||
funcp->body = command->group;
|
funcp->body = command->group;
|
||||||
#if !BB_MMU
|
# if !BB_MMU
|
||||||
funcp->body_as_string = command->group_as_string;
|
funcp->body_as_string = command->group_as_string;
|
||||||
command->group_as_string = NULL;
|
command->group_as_string = NULL;
|
||||||
#endif
|
# endif
|
||||||
command->group = NULL;
|
command->group = NULL;
|
||||||
command->argv[0] = NULL;
|
command->argv[0] = NULL;
|
||||||
debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
|
debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
|
||||||
@ -3481,7 +3506,7 @@ static int run_pipe(struct pipe *pi)
|
|||||||
enum { funcp = 0 };
|
enum { funcp = 0 };
|
||||||
#endif
|
#endif
|
||||||
char **new_env = NULL;
|
char **new_env = NULL;
|
||||||
char **old_env = NULL;
|
struct variable *old_vars = NULL;
|
||||||
|
|
||||||
if (argv[command->assignment_cnt] == NULL) {
|
if (argv[command->assignment_cnt] == NULL) {
|
||||||
/* Assignments, but no command */
|
/* Assignments, but no command */
|
||||||
@ -3529,7 +3554,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_env = putenv_all_and_save_old(new_env);
|
old_vars = set_vars_all_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]);
|
||||||
@ -3548,11 +3573,8 @@ 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_unsetenv(new_env, 1);
|
free_strings_and_unset(new_env, 1);
|
||||||
putenv_all(old_env);
|
add_vars(old_vars);
|
||||||
/* Free the pointers, but the strings themselves
|
|
||||||
* are in environ now, don't use free_strings! */
|
|
||||||
free(old_env);
|
|
||||||
clean_up_and_ret1:
|
clean_up_and_ret1:
|
||||||
free(argv_expanded);
|
free(argv_expanded);
|
||||||
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
|
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
|
||||||
@ -3567,7 +3589,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_env = putenv_all_and_save_old(new_env);
|
old_vars = set_vars_all_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);
|
||||||
@ -3592,7 +3614,7 @@ static int run_pipe(struct pipe *pi)
|
|||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
volatile nommu_save_t nommu_save;
|
volatile nommu_save_t nommu_save;
|
||||||
nommu_save.new_env = NULL;
|
nommu_save.new_env = NULL;
|
||||||
nommu_save.old_env = NULL;
|
nommu_save.old_vars = NULL;
|
||||||
nommu_save.argv = NULL;
|
nommu_save.argv = NULL;
|
||||||
nommu_save.argv_from_re_execing = NULL;
|
nommu_save.argv_from_re_execing = NULL;
|
||||||
#endif
|
#endif
|
||||||
@ -3665,11 +3687,8 @@ 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_unsetenv(nommu_save.new_env, 1);
|
free_strings_and_unset(nommu_save.new_env, 1);
|
||||||
putenv_all(nommu_save.old_env);
|
add_vars(nommu_save.old_vars);
|
||||||
/* Free the pointers, but the strings themselves
|
|
||||||
* are in environ now, don't use free_strings! */
|
|
||||||
free(nommu_save.old_env);
|
|
||||||
#endif
|
#endif
|
||||||
free(argv_expanded);
|
free(argv_expanded);
|
||||||
argv_expanded = NULL;
|
argv_expanded = NULL;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
# UNFIXED BUG
|
|
||||||
|
|
||||||
var=old
|
var=old
|
||||||
f() { echo "var=$var"; }
|
f() { echo "var=$var"; }
|
||||||
var=val f
|
var=val f
|
Loading…
Reference in New Issue
Block a user