Signed-off-by: Marcel Denia <naoir@gmx.net>lilik-openwrt-22.03
@ -0,0 +1,176 @@ | |||
BASH PATCH REPORT | |||
================= | |||
Bash-Release: 4.3 | |||
Patch-ID: bash43-027 | |||
Bug-Reported-by: Florian Weimer <fweimer@redhat.com> | |||
Bug-Reference-ID: | |||
Bug-Reference-URL: | |||
Bug-Description: | |||
This patch changes the encoding bash uses for exported functions to avoid | |||
clashes with shell variables and to avoid depending only on an environment | |||
variable's contents to determine whether or not to interpret it as a shell | |||
function. | |||
Patch (apply with `patch -p0'): | |||
--- a/variables.c | |||
+++ b/variables.c | |||
@@ -83,6 +83,11 @@ | |||
#define ifsname(s) ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0') | |||
+#define BASHFUNC_PREFIX "BASH_FUNC_" | |||
+#define BASHFUNC_PREFLEN 10 /* == strlen(BASHFUNC_PREFIX */ | |||
+#define BASHFUNC_SUFFIX "%%" | |||
+#define BASHFUNC_SUFFLEN 2 /* == strlen(BASHFUNC_SUFFIX) */ | |||
+ | |||
extern char **environ; | |||
/* Variables used here and defined in other files. */ | |||
@@ -279,7 +284,7 @@ static void push_temp_var __P((PTR_T)); | |||
static void propagate_temp_var __P((PTR_T)); | |||
static void dispose_temporary_env __P((sh_free_func_t *)); | |||
-static inline char *mk_env_string __P((const char *, const char *)); | |||
+static inline char *mk_env_string __P((const char *, const char *, int)); | |||
static char **make_env_array_from_var_list __P((SHELL_VAR **)); | |||
static char **make_var_export_array __P((VAR_CONTEXT *)); | |||
static char **make_func_export_array __P((void)); | |||
@@ -349,22 +354,33 @@ initialize_shell_variables (env, privmod | |||
/* If exported function, define it now. Don't import functions from | |||
the environment in privileged mode. */ | |||
- if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4)) | |||
+ if (privmode == 0 && read_but_dont_execute == 0 && | |||
+ STREQN (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) && | |||
+ STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) && | |||
+ STREQN ("() {", string, 4)) | |||
{ | |||
+ size_t namelen; | |||
+ char *tname; /* desired imported function name */ | |||
+ | |||
+ namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN; | |||
+ | |||
+ tname = name + BASHFUNC_PREFLEN; /* start of func name */ | |||
+ tname[namelen] = '\0'; /* now tname == func name */ | |||
+ | |||
string_length = strlen (string); | |||
- temp_string = (char *)xmalloc (3 + string_length + char_index); | |||
+ temp_string = (char *)xmalloc (namelen + string_length + 2); | |||
- strcpy (temp_string, name); | |||
- temp_string[char_index] = ' '; | |||
- strcpy (temp_string + char_index + 1, string); | |||
+ memcpy (temp_string, tname, namelen); | |||
+ temp_string[namelen] = ' '; | |||
+ memcpy (temp_string + namelen + 1, string, string_length + 1); | |||
/* Don't import function names that are invalid identifiers from the | |||
environment, though we still allow them to be defined as shell | |||
variables. */ | |||
- if (legal_identifier (name)) | |||
- parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD); | |||
+ if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname))) | |||
+ parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD); | |||
- if (temp_var = find_function (name)) | |||
+ if (temp_var = find_function (tname)) | |||
{ | |||
VSETATTR (temp_var, (att_exported|att_imported)); | |||
array_needs_making = 1; | |||
@@ -377,8 +393,11 @@ initialize_shell_variables (env, privmod | |||
array_needs_making = 1; | |||
} | |||
last_command_exit_value = 1; | |||
- report_error (_("error importing function definition for `%s'"), name); | |||
+ report_error (_("error importing function definition for `%s'"), tname); | |||
} | |||
+ | |||
+ /* Restore original suffix */ | |||
+ tname[namelen] = BASHFUNC_SUFFIX[0]; | |||
} | |||
#if defined (ARRAY_VARS) | |||
# if ARRAY_EXPORT | |||
@@ -2954,7 +2973,7 @@ assign_in_env (word, flags) | |||
var->context = variable_context; /* XXX */ | |||
INVALIDATE_EXPORTSTR (var); | |||
- var->exportstr = mk_env_string (name, value); | |||
+ var->exportstr = mk_env_string (name, value, 0); | |||
array_needs_making = 1; | |||
@@ -3852,21 +3871,42 @@ merge_temporary_env () | |||
/* **************************************************************** */ | |||
static inline char * | |||
-mk_env_string (name, value) | |||
+mk_env_string (name, value, isfunc) | |||
const char *name, *value; | |||
+ int isfunc; | |||
{ | |||
- int name_len, value_len; | |||
- char *p; | |||
+ size_t name_len, value_len; | |||
+ char *p, *q; | |||
name_len = strlen (name); | |||
value_len = STRLEN (value); | |||
- p = (char *)xmalloc (2 + name_len + value_len); | |||
- strcpy (p, name); | |||
- p[name_len] = '='; | |||
+ | |||
+ /* If we are exporting a shell function, construct the encoded function | |||
+ name. */ | |||
+ if (isfunc && value) | |||
+ { | |||
+ p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2); | |||
+ q = p; | |||
+ memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN); | |||
+ q += BASHFUNC_PREFLEN; | |||
+ memcpy (q, name, name_len); | |||
+ q += name_len; | |||
+ memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN); | |||
+ q += BASHFUNC_SUFFLEN; | |||
+ } | |||
+ else | |||
+ { | |||
+ p = (char *)xmalloc (2 + name_len + value_len); | |||
+ memcpy (p, name, name_len); | |||
+ q = p + name_len; | |||
+ } | |||
+ | |||
+ q[0] = '='; | |||
if (value && *value) | |||
- strcpy (p + name_len + 1, value); | |||
+ memcpy (q + 1, value, value_len + 1); | |||
else | |||
- p[name_len + 1] = '\0'; | |||
+ q[1] = '\0'; | |||
+ | |||
return (p); | |||
} | |||
@@ -3952,7 +3992,7 @@ make_env_array_from_var_list (vars) | |||
/* Gee, I'd like to get away with not using savestring() if we're | |||
using the cached exportstr... */ | |||
list[list_index] = USE_EXPORTSTR ? savestring (value) | |||
- : mk_env_string (var->name, value); | |||
+ : mk_env_string (var->name, value, function_p (var)); | |||
if (USE_EXPORTSTR == 0) | |||
SAVE_EXPORTSTR (var, list[list_index]); | |||
--- a/patchlevel.h | |||
+++ b/patchlevel.h | |||
@@ -25,6 +25,6 @@ | |||
regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh | |||
looks for to find the patch level (for the sccs version string). */ | |||
-#define PATCHLEVEL 26 | |||
+#define PATCHLEVEL 27 | |||
#endif /* _PATCHLEVEL_H_ */ |
@ -0,0 +1,50 @@ | |||
BASH PATCH REPORT | |||
================= | |||
Bash-Release: 4.3 | |||
Patch-ID: bash43-029 | |||
Bug-Reported-by: Michal Zalewski <lcamtuf@coredump.cx> | |||
Bug-Reference-ID: | |||
Bug-Reference-URL: | |||
Bug-Description: | |||
When bash is parsing a function definition that contains a here-document | |||
delimited by end-of-file (or end-of-string), it leaves the closing delimiter | |||
uninitialized. This can result in an invalid memory access when the parsed | |||
function is later copied. | |||
Patch (apply with `patch -p0'): | |||
--- a/make_cmd.c | |||
+++ b/make_cmd.c | |||
@@ -692,6 +692,7 @@ make_redirection (source, instruction, d | |||
/* First do the common cases. */ | |||
temp->redirector = source; | |||
temp->redirectee = dest_and_filename; | |||
+ temp->here_doc_eof = 0; | |||
temp->instruction = instruction; | |||
temp->flags = 0; | |||
temp->rflags = flags; | |||
--- a/copy_cmd.c | |||
+++ b/copy_cmd.c | |||
@@ -126,7 +126,7 @@ copy_redirect (redirect) | |||
{ | |||
case r_reading_until: | |||
case r_deblank_reading_until: | |||
- new_redirect->here_doc_eof = savestring (redirect->here_doc_eof); | |||
+ new_redirect->here_doc_eof = redirect->here_doc_eof ? savestring (redirect->here_doc_eof) : 0; | |||
/*FALLTHROUGH*/ | |||
case r_reading_string: | |||
case r_appending_to: | |||
--- a/patchlevel.h | |||
+++ b/patchlevel.h | |||
@@ -25,6 +25,6 @@ | |||
regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh | |||
looks for to find the patch level (for the sccs version string). */ | |||
-#define PATCHLEVEL 28 | |||
+#define PATCHLEVEL 29 | |||
#endif /* _PATCHLEVEL_H_ */ |
@ -1,83 +0,0 @@ | |||
--- a/parse.y | |||
+++ b/parse.y | |||
@@ -265,9 +265,21 @@ int parser_state; | |||
/* Variables to manage the task of reading here documents, because we need to | |||
defer the reading until after a complete command has been collected. */ | |||
-static REDIRECT *redir_stack[10]; | |||
+static REDIRECT **redir_stack; | |||
int need_here_doc; | |||
+/* Pushes REDIR onto redir_stack, resizing it as needed. */ | |||
+static void | |||
+push_redir_stack (REDIRECT *redir) | |||
+{ | |||
+ /* Guard against oveflow. */ | |||
+ if (need_here_doc + 1 > INT_MAX / sizeof (*redir_stack)) | |||
+ abort (); | |||
+ redir_stack = xrealloc (redir_stack, | |||
+ (need_here_doc + 1) * sizeof (*redir_stack)); | |||
+ redir_stack[need_here_doc++] = redir; | |||
+} | |||
+ | |||
/* Where shell input comes from. History expansion is performed on each | |||
line when the shell is interactive. */ | |||
static char *shell_input_line = (char *)NULL; | |||
@@ -520,42 +532,42 @@ redirection: '>' WORD | |||
source.dest = 0; | |||
redir.filename = $2; | |||
$$ = make_redirection (source, r_reading_until, redir, 0); | |||
- redir_stack[need_here_doc++] = $$; | |||
+ push_redir_stack ($$); | |||
} | |||
| NUMBER LESS_LESS WORD | |||
{ | |||
source.dest = $1; | |||
redir.filename = $3; | |||
$$ = make_redirection (source, r_reading_until, redir, 0); | |||
- redir_stack[need_here_doc++] = $$; | |||
+ push_redir_stack ($$); | |||
} | |||
| REDIR_WORD LESS_LESS WORD | |||
{ | |||
source.filename = $1; | |||
redir.filename = $3; | |||
$$ = make_redirection (source, r_reading_until, redir, REDIR_VARASSIGN); | |||
- redir_stack[need_here_doc++] = $$; | |||
+ push_redir_stack ($$); | |||
} | |||
| LESS_LESS_MINUS WORD | |||
{ | |||
source.dest = 0; | |||
redir.filename = $2; | |||
$$ = make_redirection (source, r_deblank_reading_until, redir, 0); | |||
- redir_stack[need_here_doc++] = $$; | |||
+ push_redir_stack ($$); | |||
} | |||
| NUMBER LESS_LESS_MINUS WORD | |||
{ | |||
source.dest = $1; | |||
redir.filename = $3; | |||
$$ = make_redirection (source, r_deblank_reading_until, redir, 0); | |||
- redir_stack[need_here_doc++] = $$; | |||
+ push_redir_stack ($$); | |||
} | |||
| REDIR_WORD LESS_LESS_MINUS WORD | |||
{ | |||
source.filename = $1; | |||
redir.filename = $3; | |||
$$ = make_redirection (source, r_deblank_reading_until, redir, REDIR_VARASSIGN); | |||
- redir_stack[need_here_doc++] = $$; | |||
+ push_redir_stack ($$); | |||
} | |||
| LESS_LESS_LESS WORD | |||
{ | |||
@@ -4905,7 +4917,7 @@ got_token: | |||
case CASE: | |||
case SELECT: | |||
case FOR: | |||
- if (word_top < MAX_CASE_NEST) | |||
+ if (word_top + 1 < MAX_CASE_NEST) | |||
word_top++; | |||
word_lineno[word_top] = line_number; | |||
break; |
@ -1,148 +0,0 @@ | |||
--- a/variables.c | |||
+++ b/variables.c | |||
@@ -279,7 +279,7 @@ static void push_temp_var __P((PTR_T)); | |||
static void propagate_temp_var __P((PTR_T)); | |||
static void dispose_temporary_env __P((sh_free_func_t *)); | |||
-static inline char *mk_env_string __P((const char *, const char *)); | |||
+static inline char *mk_env_string __P((const char *, const char *, int)); | |||
static char **make_env_array_from_var_list __P((SHELL_VAR **)); | |||
static char **make_var_export_array __P((VAR_CONTEXT *)); | |||
static char **make_func_export_array __P((void)); | |||
@@ -312,6 +312,14 @@ create_variable_tables () | |||
#endif | |||
} | |||
+/* Prefix and suffix for environment variable names which contain | |||
+ shell functions. */ | |||
+#define FUNCDEF_PREFIX "BASH_FUNC_" | |||
+#define FUNCDEF_PREFIX_LEN (strlen (FUNCDEF_PREFIX)) | |||
+#define FUNCDEF_SUFFIX "()" | |||
+#define FUNCDEF_SUFFIX_LEN (strlen (FUNCDEF_SUFFIX)) | |||
+ | |||
+ | |||
/* Initialize the shell variables from the current environment. | |||
If PRIVMODE is nonzero, don't import functions from ENV or | |||
parse $SHELLOPTS. */ | |||
@@ -349,22 +357,31 @@ initialize_shell_variables (env, privmod | |||
/* If exported function, define it now. Don't import functions from | |||
the environment in privileged mode. */ | |||
- if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4)) | |||
- { | |||
- string_length = strlen (string); | |||
- temp_string = (char *)xmalloc (3 + string_length + char_index); | |||
+ if (privmode == 0 && read_but_dont_execute == 0 | |||
+ && STREQN (FUNCDEF_PREFIX, name, FUNCDEF_PREFIX_LEN) | |||
+ && STREQ (name + char_index - FUNCDEF_SUFFIX_LEN, FUNCDEF_SUFFIX) | |||
+ && STREQN ("() {", string, 4)) | |||
+ { | |||
+ size_t name_length | |||
+ = char_index - (FUNCDEF_PREFIX_LEN + FUNCDEF_SUFFIX_LEN); | |||
+ char *temp_name = name + FUNCDEF_PREFIX_LEN; | |||
+ /* Temporarily remove the suffix. */ | |||
+ temp_name[name_length] = '\0'; | |||
- strcpy (temp_string, name); | |||
- temp_string[char_index] = ' '; | |||
- strcpy (temp_string + char_index + 1, string); | |||
+ string_length = strlen (string); | |||
+ temp_string = (char *)xmalloc (name_length + 1 + string_length + 1); | |||
+ memcpy (temp_string, temp_name, name_length); | |||
+ temp_string[name_length] = ' '; | |||
+ memcpy (temp_string + name_length + 1, string, string_length + 1); | |||
/* Don't import function names that are invalid identifiers from the | |||
environment, though we still allow them to be defined as shell | |||
variables. */ | |||
- if (legal_identifier (name)) | |||
- parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD); | |||
+ if (legal_identifier (temp_name)) | |||
+ parse_and_execute (temp_string, temp_name, | |||
+ SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD); | |||
- if (temp_var = find_function (name)) | |||
+ if (temp_var = find_function (temp_name)) | |||
{ | |||
VSETATTR (temp_var, (att_exported|att_imported)); | |||
array_needs_making = 1; | |||
@@ -379,6 +396,9 @@ initialize_shell_variables (env, privmod | |||
last_command_exit_value = 1; | |||
report_error (_("error importing function definition for `%s'"), name); | |||
} | |||
+ | |||
+ /* Restore the original suffix. */ | |||
+ temp_name[name_length] = FUNCDEF_SUFFIX[0]; | |||
} | |||
#if defined (ARRAY_VARS) | |||
# if ARRAY_EXPORT | |||
@@ -2954,7 +2974,7 @@ assign_in_env (word, flags) | |||
var->context = variable_context; /* XXX */ | |||
INVALIDATE_EXPORTSTR (var); | |||
- var->exportstr = mk_env_string (name, value); | |||
+ var->exportstr = mk_env_string (name, value, 0); | |||
array_needs_making = 1; | |||
@@ -3851,22 +3871,43 @@ merge_temporary_env () | |||
/* */ | |||
/* **************************************************************** */ | |||
+/* Returns the string NAME=VALUE if !FUNCTIONP or if VALUE == NULL (in | |||
+ which case it is treated as empty). Otherwise, decorate NAME with | |||
+ FUNCDEF_PREFIX and FUNCDEF_SUFFIX, and return a string of the form | |||
+ FUNCDEF_PREFIX NAME FUNCDEF_SUFFIX = VALUE (without spaces). */ | |||
static inline char * | |||
-mk_env_string (name, value) | |||
+mk_env_string (name, value, functionp) | |||
const char *name, *value; | |||
+ int functionp; | |||
{ | |||
- int name_len, value_len; | |||
- char *p; | |||
+ size_t name_len, value_len; | |||
+ char *p, *q; | |||
name_len = strlen (name); | |||
value_len = STRLEN (value); | |||
- p = (char *)xmalloc (2 + name_len + value_len); | |||
- strcpy (p, name); | |||
- p[name_len] = '='; | |||
+ if (functionp && value != NULL) | |||
+ { | |||
+ p = (char *)xmalloc (FUNCDEF_PREFIX_LEN + name_len + FUNCDEF_SUFFIX_LEN | |||
+ + 1 + value_len + 1); | |||
+ q = p; | |||
+ memcpy (q, FUNCDEF_PREFIX, FUNCDEF_PREFIX_LEN); | |||
+ q += FUNCDEF_PREFIX_LEN; | |||
+ memcpy (q, name, name_len); | |||
+ q += name_len; | |||
+ memcpy (q, FUNCDEF_SUFFIX, FUNCDEF_SUFFIX_LEN); | |||
+ q += FUNCDEF_SUFFIX_LEN; | |||
+ } | |||
+ else | |||
+ { | |||
+ p = (char *)xmalloc (name_len + 1 + value_len + 1); | |||
+ memcpy (p, name, name_len); | |||
+ q = p + name_len; | |||
+ } | |||
+ q[0] = '='; | |||
if (value && *value) | |||
- strcpy (p + name_len + 1, value); | |||
+ memcpy (q + 1, value, value_len + 1); | |||
else | |||
- p[name_len + 1] = '\0'; | |||
+ q[1] = '\0'; | |||
return (p); | |||
} | |||
@@ -3952,7 +3993,7 @@ make_env_array_from_var_list (vars) | |||
/* Gee, I'd like to get away with not using savestring() if we're | |||
using the cached exportstr... */ | |||
list[list_index] = USE_EXPORTSTR ? savestring (value) | |||
- : mk_env_string (var->name, value); | |||
+ : mk_env_string (var->name, value, function_p (var)); | |||
if (USE_EXPORTSTR == 0) | |||
SAVE_EXPORTSTR (var, list[list_index]); |