From 81f7b44564cd1510788035cea7c59631865a7db2 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Mon, 26 Jun 2023 16:23:10 -0400 Subject: [PATCH] fix for -c command ending with backslash; fix several small memory leaks; fix several uninitialized variables; compgen -V fix --- CWRU/CWRU.chlog | 79 +++++++++++++++++++++++++++++++++++++++ arrayfunc.c | 6 ++- arrayfunc.h | 1 + bashline.c | 1 + builtins/cd.def | 5 +-- builtins/complete.def | 7 +--- builtins/evalstring.c | 6 +++ builtins/read.def | 10 +++-- examples/loadables/kv.c | 2 +- examples/loadables/stat.c | 1 + lib/readline/complete.c | 2 +- lib/readline/input.c | 1 + lib/readline/kill.c | 2 +- lib/sh/anonfile.c | 6 ++- parse.y | 18 +++++++++ subst.c | 4 +- 16 files changed, 133 insertions(+), 18 deletions(-) diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 5e88f380..cb9fb332 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -6797,3 +6797,82 @@ lib/sh/anonfile.c - anonopen: use memfd_create if it is available, fall through to traditional Unix/POSIX implementation if it fails + 6/20 + ---- +parse.y + - yy_string_unget: don't push EOF back to a string, like the other + unget functions + + 6/21 + ---- +parse.y + - read_a_line: don't push an EOF back into the string or line if the + line ends in a backslash. + From a report by Rob Landley + + 6/22 + ---- +lib/readline/input.c + - rl_gather_tyi: make sure result is initialized + +lib/readline/kill.c + - _rl_read_bracketed_paste_prefix: make sure key is initialized + +builtins/read.def + - read_builtin: make sure pass_next and saw_escape are initialized + before a possible goto + - read_builtin: make sure to initialize prevset for very short + timeouts + +subst.c + - function_substitute: make sure tflag is initialized even when we + don't call read_comsub, since we return it in ret->flags + All from a report by Grisha Levit + +builtins/complete.def + - compgen_builtin: use array_from_argv to assign elements of the + stringlist to the array variable. + Update from Grisha Levit + +builtins/cd.def + - bindpwd: fix seg fault from an unlikely set of circumstances + From a report by Grisha Levit + +arrayfunc.h + - convert_validarray_flags_to_arrayval_flags: initialize avflags + From a report by Grisha Levit + +lib/sh/anonfile.c + - anonopen: set *fn if memfd_create is used + From a report by Grisha Levit + + 6/23 + ---- +arrayfunc.c + - bind_assoc_var_internal: free key to fix small leak if assign_func + used + - quote_compound_array_word: free value to fix small leak + +builtins/evalstring.c + - parse_and_execute: free parsed command on failed function definition + import + - open_redir_file: free FN if we're not passing it back to the caller + +subst.c + - param_expand: free TEMP1 in code paths that don't do it now + + +bashline.c + - bash_command_name_stat_hook: if we modify *NAME, free the old value + +examples/loadables/{kv,stat}.c + - bind_assoc_variable is caller-free VALUE, so free when needed and + don't allocate a new copy if not + All from a report by Grisha Levit + + 6/24 + ---- +lib/readline/complete.c + - rl_menu_complete: use _rl_free_match_list instead of just freeing + MATCHES if we have too many possible completions to display. + From a report by Grisha Levit diff --git a/arrayfunc.c b/arrayfunc.c index 74ff6316..03eefe5a 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -206,7 +206,10 @@ bind_assoc_var_internal (SHELL_VAR *entry, HASH_TABLE *hash, char *key, const ch newval = make_array_variable_value (entry, 0, key, value, flags); if (entry->assign_func) - (*entry->assign_func) (entry, newval, 0, key); + { + (*entry->assign_func) (entry, newval, 0, key); + FREE (key); + } else assoc_insert (hash, key, newval); @@ -958,6 +961,7 @@ quote_compound_array_word (char *w, int type) if (t != w+ind) free (t); strcpy (nword + i, value); + free (value); return nword; } diff --git a/arrayfunc.h b/arrayfunc.h index 08458c54..34f35b33 100644 --- a/arrayfunc.h +++ b/arrayfunc.h @@ -163,6 +163,7 @@ convert_validarray_flags_to_arrayval_flags (int vflags) { int avflags; + avflags = 0; if (vflags & VA_NOEXPAND) avflags |= AV_NOEXPAND; if (vflags & VA_ONEWORD) diff --git a/bashline.c b/bashline.c index 80f68603..02f36e3a 100644 --- a/bashline.c +++ b/bashline.c @@ -1922,6 +1922,7 @@ bash_command_name_stat_hook (char **name) result = search_for_command (cname, 0); if (result) { + FREE (*name); *name = result; return 1; } diff --git a/builtins/cd.def b/builtins/cd.def index de123f8b..e3156463 100644 --- a/builtins/cd.def +++ b/builtins/cd.def @@ -158,10 +158,9 @@ bindpwd (int no_symlinks) pwdvar = get_string_value ("PWD"); tvar = bind_variable ("OLDPWD", pwdvar, 0); - if (tvar && readonly_p (tvar)) + if (tvar == 0 || readonly_p (tvar)) r = EXECUTION_FAILURE; - - if (old_anm == 0 && array_needs_making && exported_p (tvar)) + else if (old_anm == 0 && array_needs_making && exported_p (tvar)) { update_export_env_inplace ("OLDPWD=", 7, pwdvar); array_needs_making = 0; diff --git a/builtins/complete.def b/builtins/complete.def index 890cf20d..74d966d5 100644 --- a/builtins/complete.def +++ b/builtins/complete.def @@ -678,7 +678,6 @@ compgen_builtin (WORD_LIST *list) int old_ind, old_completion, old_quoting, old_suppress; SHELL_VAR *var; char *varname; - WORD_LIST *alist; if (list == 0) return (EXECUTION_SUCCESS); @@ -763,11 +762,7 @@ compgen_builtin (WORD_LIST *list) var = builtin_find_indexed_array (varname, 1); if (var && sl && sl->list && sl->list_len) { - alist = strlist_to_word_list (sl, 0, 0); - assign_array_var_from_word_list (var, alist, 0); - free (sl); - sl = (STRINGLIST *)NULL; - dispose_words (alist); + array_from_argv (array_cell (var), sl->list, sl->list_len); rval = EXECUTION_SUCCESS; } } diff --git a/builtins/evalstring.c b/builtins/evalstring.c index 11785de1..41198a94 100644 --- a/builtins/evalstring.c +++ b/builtins/evalstring.c @@ -415,6 +415,7 @@ parse_and_execute (char *string, const char *from_file, int flags) run_unwind_frame ("pe_dispose"); last_result = last_command_exit_value = EXECUTION_FAILURE; /* XXX */ set_pipestatus_from_exit (last_command_exit_value); + if (subshell_environment) { should_jump_to_top_level = 1; @@ -468,6 +469,8 @@ parse_and_execute (char *string, const char *from_file, int flags) should_jump_to_top_level = 0; last_result = last_command_exit_value = EX_BADUSAGE; set_pipestatus_from_exit (last_command_exit_value); + dispose_command (command); + global_command = (COMMAND *)NULL; reset_parser (); break; } @@ -766,6 +769,9 @@ open_redir_file (REDIRECT *r, char **fnp) if (fnp) *fnp = fn; + else + free (fn); + return fd; } diff --git a/builtins/read.def b/builtins/read.def index cb4e1e59..5b2621fe 100644 --- a/builtins/read.def +++ b/builtins/read.def @@ -403,6 +403,9 @@ read_builtin (WORD_LIST *list) input_string = (char *)xmalloc (size = 112); /* XXX was 128 */ input_string[0] = '\0'; + pass_next = 0; /* Non-zero signifies last char was backslash. */ + saw_escape = 0; /* Non-zero signifies that we saw an escape char */ + /* More input and options validation */ if (nflag == 1 && nchars == 0) { @@ -428,6 +431,8 @@ read_builtin (WORD_LIST *list) sigemptyset (&chldset); sigprocmask (SIG_BLOCK, (sigset_t *)0, &chldset); sigaddset (&chldset, SIGCHLD); + sigemptyset (&prevset); + sigprocmask (SIG_SETMASK, (sigset_t *)0, &prevset); #endif begin_unwind_frame ("read_builtin"); @@ -463,9 +468,6 @@ read_builtin (WORD_LIST *list) add_unwind_protect (xfree, rlbuf); #endif - pass_next = 0; /* Non-zero signifies last char was backslash. */ - saw_escape = 0; /* Non-zero signifies that we saw an escape char */ - if (tmsec > 0 || tmusec > 0) { /* Turn off the timeout if stdin is a regular file (e.g. from @@ -495,7 +497,9 @@ read_builtin (WORD_LIST *list) if (code) { reset_timeout (); +#if defined (SIGCHLD) sigprocmask (SIG_SETMASK, &prevset, (sigset_t *)0); +#endif /* Tricky. The top of the unwind-protect stack is the free of input_string. We want to run all the rest and use input_string, diff --git a/examples/loadables/kv.c b/examples/loadables/kv.c index 1dfceb6a..ee6dc7f0 100644 --- a/examples/loadables/kv.c +++ b/examples/loadables/kv.c @@ -71,7 +71,7 @@ kvsplit (SHELL_VAR *v, char *line, char *dstring) else value = ""; - return (bind_assoc_variable (v, name_cell (v), savestring (key), savestring (value), 0) != 0); + return (bind_assoc_variable (v, name_cell (v), savestring (key), value, 0) != 0); } int diff --git a/examples/loadables/stat.c b/examples/loadables/stat.c index 1093f7b0..d58f07e5 100644 --- a/examples/loadables/stat.c +++ b/examples/loadables/stat.c @@ -330,6 +330,7 @@ loadstat (char *vname, SHELL_VAR *var, char *fname, int flags, char *fmt, struct key = savestring (arraysubs[i]); value = statval (i, fname, flags, fmt, sp); v = bind_assoc_variable (var, vname, key, value, ASS_FORCE); + free (value); } return 0; } diff --git a/lib/readline/complete.c b/lib/readline/complete.c index 5aa97fd5..05779f97 100644 --- a/lib/readline/complete.c +++ b/lib/readline/complete.c @@ -2868,7 +2868,7 @@ rl_menu_complete (int count, int ignore) if (rl_completion_query_items > 0 && match_list_size >= rl_completion_query_items) { rl_ding (); - FREE (matches); + _rl_free_match_list (matches); matches = (char **)0; full_completion = 1; return (0); diff --git a/lib/readline/input.c b/lib/readline/input.c index 229474ff..98074530 100644 --- a/lib/readline/input.c +++ b/lib/readline/input.c @@ -249,6 +249,7 @@ rl_gather_tyi (void) struct timeval timeout; #endif + result = -1; chars_avail = 0; input = 0; tty = fileno (rl_instream); diff --git a/lib/readline/kill.c b/lib/readline/kill.c index 0ba59ce5..972c7d9e 100644 --- a/lib/readline/kill.c +++ b/lib/readline/kill.c @@ -779,7 +779,7 @@ _rl_read_bracketed_paste_prefix (int c) pbpref = BRACK_PASTE_PREF; /* XXX - debugging */ if (c != pbpref[0]) return (0); - pbuf[ind = 0] = c; + pbuf[ind = 0] = key = c; while (ind < BRACK_PASTE_SLEN-1 && (RL_ISSTATE (RL_STATE_INPUTPENDING|RL_STATE_MACROINPUT) == 0) && _rl_pushed_input_available () == 0 && diff --git a/lib/sh/anonfile.c b/lib/sh/anonfile.c index 560b8c86..9a805b2d 100644 --- a/lib/sh/anonfile.c +++ b/lib/sh/anonfile.c @@ -55,7 +55,11 @@ anonopen (const char *name, int flags, char **fn) /* "Names do not affect the behavior of the file descriptor." */ fd = memfd_create ("anonopen", 0); if (fd >= 0) - return fd; + { + if (fn) + *fn = 0; + return fd; + } /* If memfd_create fails, we fall through to the unlinked-regular-file implementation. */ #endif diff --git a/parse.y b/parse.y index c08b77f6..6701d596 100644 --- a/parse.y +++ b/parse.y @@ -1696,6 +1696,9 @@ yy_string_get (void) static int yy_string_unget (int c) { + if (c == EOF && *bash_input.location.string == '\0') + return EOF; + *(--bash_input.location.string) = c; return (c); } @@ -2162,6 +2165,21 @@ read_a_line (int remove_quoted_newline) line_number++; continue; /* Make the unquoted \ pair disappear. */ } + else if (peekc == EOF) + { + /* Don't push EOF back. */ +#if 1 + /* Leave this in for now, relies on how the expansion code + treats an unescaped backslash at the end of a word. */ + RESIZE_MALLOCED_BUFFER (line_buffer, indx, 2, buffer_size, 128); + if (expanding_alias() == 0 && + (bash_input.type == st_string || bash_input.type == st_bstream || + (interactive == 0 && bash_input.type == st_stream))) + line_buffer[indx++] = '\\'; /* like below in shell_getc */ +#endif + line_buffer[indx++] = c; + c = '\n'; /* force break below */ + } else { yy_ungetc (peekc); diff --git a/subst.c b/subst.c index 11e7a00c..215e3469 100644 --- a/subst.c +++ b/subst.c @@ -7016,12 +7016,12 @@ function_substitute (char *string, int quoted, int flags) /* rewind back to the start of the file and read the contents */ + tflag = 0; if (valsub == 0) { /* We call anonclose as part of the outer nofork unwind-protects */ BLOCK_SIGNAL (SIGINT, set, oset); lseek (afd, 0, SEEK_SET); - tflag = 0; istring = read_comsub (afd, quoted, flags, &tflag); UNBLOCK_SIGNAL (oset); } @@ -10840,6 +10840,7 @@ comsub: { chk_atstar (temp, quoted, pflags, quoted_dollar_at_p, contains_dollar_at); tdesc = parameter_brace_expand_word (temp, SPECIAL_VAR (temp, 0), quoted, pflags, 0); + free (temp1); if (tdesc == &expand_wdesc_error || tdesc == &expand_wdesc_fatal) return (tdesc); ret = tdesc; @@ -10852,6 +10853,7 @@ comsub: { set_exit_status (EXECUTION_FAILURE); report_error (_("%s: invalid variable name for name reference"), temp); + free (temp1); return (&expand_wdesc_error); /* XXX */ } else