From: Chet Ramey Date: Sat, 27 Jan 2024 21:28:31 +0000 (-0500) Subject: make completion of nofork comsubs a little better; fix leaks on signals for non-incre... X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=138f3cc3591163d18ee4b6390ecd6894d5d16977;p=thirdparty%2Fbash.git make completion of nofork comsubs a little better; fix leaks on signals for non-incremental searches; fix leaks in readstr interface --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 9912f7273..fc38e0baf 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -8371,3 +8371,68 @@ subst.c - function_substitute: unwind-protect current_builtin and this_shell_builtin like we do this_shell_function From a fuzzing report by Nathan Mills + + 1/24 + ---- +braces.c + - brace_gobbler: handle nofork command substitutions; skip over any + enclosed command and any braces it contains + +bashline.c + - check_redir: return 0 if we're in a nofork comsub, so we will attempt + command word completion if appropriate + + 1/26 + ---- +lib/readline/readline.h + - RL_STATE_READSTR: new state flag saying we are reading an arbitrary + string from the keyboard using rl_readstr + +lib/readline/rlprivate.h + - READSTR_FREEPMT: new context state flag for rl_readstr; indicates + that we have allocated a new prompt with _rl_make_prompt_for_search + and we should free it with rl_restore_prompt because rl_clear_message + will not do it + +lib/readline/text.c + - _rl_readstr_init,_rl_readstr_cleanup: set and unset RL_STATE_READSTR + - _rl_unsave_saved_readstr_line: free any rl_undo_list, which we may + have accumulated while reading the string, before restoring the line + Fixes leaks reported by sparrowhawk996@gmail.com. + - _rl_readstr_init: set the READSTR_FREEPMT context flag after we + call _rl_make_prompt_for_search, which calls rl_save_prompt + - _rl_readstr_sigcleanup: new function to call from signal cleanup; + restores prompt and calls _rl_readstr_cleanup to free the readstr + context + - _rl_readstr_restore: if the RL_READSTR_FREEPMT flag is set in the + context, call rl_restore_prompt to deallocate the prompt we created + +lib/readline/callback.c + - rl_callback_sigcleanup: call _rl_readstr_sigcleanup to deallocate + readstr state on a signal + +lib/readline/text.c + - rl_execute_named_command: free COMMAND before returning + + 1/27 + ---- +lib/readline/rlprivate.h + - SF_FREEPMT: new flag for non-incremental search contexts: means we + saved the prompt data with _rl_make_prompt_for_search and need to + restore it with rl_restore_prompt, since rl_clear_message will not + +lib/readline/search.c + - _rl_nsearch_sigcleanup: new function that if calls rl_restore_prompt + to clean up the saved prompt data if necessary before calling the + context cleanup function, avoids memory leak + - _rl_nsearch_init: set the SF_FREEPMPT flag after calling + _rl_make_prompt_from_search so we can clean it up properly on errors + - _rl_nsearch_abort,_rl_nsearch_dosearch: call rl_restore_prompt only + if cxt->sflags includes SF_FREEPMT, manage SF_FREEPMT state + +lib/readline/signals.c + - _rl_state_sigcleanup: call _rl_nsearch_sigcleanup + +lib/readline/callback.c + - _rl_callback_sigcleanup: call _rl_nsearch_sigcleanup + diff --git a/bashline.c b/bashline.c index 8ba34a816..c11484ecf 100644 --- a/bashline.c +++ b/bashline.c @@ -1385,17 +1385,18 @@ bash_spell_correct_shellword (int count, int key) static inline int check_redir (int ti) { - register int this_char, prev_char; + int this_char, prev_char, next_char; /* Handle the two character tokens `>&', `<&', and `>|'. We are not in a command position after one of these. */ this_char = rl_line_buffer[ti]; prev_char = (ti > 0) ? rl_line_buffer[ti - 1] : 0; + next_char = (ti < rl_end) ? rl_line_buffer[ti + 1] : 0; if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) || (this_char == '|' && prev_char == '>')) return (1); - else if (this_char == '{' && prev_char == '$') /*}*/ + else if (this_char == '{' && prev_char == '$' && FUNSUB_CHAR (next_char) == 0) /*}*/ return (1); #if 0 /* Not yet */ else if (this_char == '(' && prev_char == '$') /*)*/ @@ -1789,6 +1790,8 @@ bash_default_completion (const char *text, int start, int end, int qc, int compf { if (qc != '\'' && text[1] == '(') /* ) */ matches = rl_completion_matches (text, command_subst_completion_function); + else if (qc != '\'' && text[1] == '{' && FUNSUB_CHAR (text[2])) /* } */ + matches = rl_completion_matches (text, command_subst_completion_function); else { matches = rl_completion_matches (text, variable_completion_function); @@ -2401,6 +2404,8 @@ command_subst_completion_function (const char *text, int state) text++; else if (*text == '$' && text[1] == '(') /* ) */ text += 2; + else if (*text == '$' && text[1] == '{' && FUNSUB_CHAR (text[2])) /*}*/ + text += 3; /* nofork command substitution */ /* If the text was quoted, suppress any quote character that the readline completion code would insert. */ rl_completion_suppress_quote = 1; diff --git a/braces.c b/braces.c index bef62ea38..e2517c981 100644 --- a/braces.c +++ b/braces.c @@ -38,6 +38,7 @@ #if defined (SHELL) # include "shell.h" +# include "parser.h" /* FUNSUB_CHAR */ #else # if defined (TEST) typedef char *WORD_DESC; @@ -619,33 +620,34 @@ brace_gobbler (char *text, size_t tlen, int *indx, int satisfy) #if defined (SHELL) /* If compiling for the shell, treat ${...} like \{...} */ - if (c == '$' && text[i+1] == '{' && quoted != '\'') /* } */ + if (c == '$' && i < tlen && text[i+1] == '{' && quoted != '\'') /* } */ { -#if 0 - /* If we want to inhibit brace expansion during parameter expansions, - we need to skip over parameter expansions here. This is easier - than teaching brace expansion about the idiosyncracies of shell - word expansion. */ - int o, f; - o = no_longjmp_on_fatal_error; - no_longjmp_on_fatal_error = 1; - f = (quoted == '"') ? Q_DOUBLE_QUOTES : 0; - si = i + 2; - t = extract_dollar_brace_string (text, &si, f, SX_NOALLOC|SX_NOLONGJMP|SX_COMPLETE|SX_NOERROR); - i = si + 1; - no_longjmp_on_fatal_error = o; - if (i > tlen) +#if 1 + /* nofork command substitution */ + if (i < tlen - 1 && FUNSUB_CHAR (text[i+2])) { - i = tlen; - c = 0; - break; + int o, f; +funsub: + o = no_longjmp_on_fatal_error; + no_longjmp_on_fatal_error = 1; + f = (quoted == '"') ? Q_DOUBLE_QUOTES : 0; + si = i + 2; + t = extract_function_subst (text, &si, f, SX_NOALLOC|SX_NOLONGJMP|SX_NOERROR); + i = si + 1; + no_longjmp_on_fatal_error = o; + if (i > tlen) + { + i = tlen; + c = 0; + break; + } + continue; } -#else +#endif pass_next = 1; i++; if (quoted == 0) level++; -#endif continue; } #endif @@ -656,7 +658,7 @@ brace_gobbler (char *text, size_t tlen, int *indx, int satisfy) quoted = 0; #if defined (SHELL) /* The shell allows quoted command substitutions */ - if (quoted == '"' && c == '$' && text[i+1] == '(') /*)*/ + if (quoted == '"' && c == '$' && i < tlen && text[i+1] == '(') /*)*/ goto comsub; #endif #if defined (SHELL) @@ -676,7 +678,7 @@ brace_gobbler (char *text, size_t tlen, int *indx, int satisfy) #if defined (SHELL) /* Pass new-style command and process substitutions through unchanged. */ - if ((c == '$' || c == '<' || c == '>') && text[i+1] == '(') /* ) */ + if ((c == '$' || c == '<' || c == '>') && i < tlen && text[i+1] == '(') /* ) */ { int o; @@ -684,11 +686,7 @@ comsub: o = no_longjmp_on_fatal_error; no_longjmp_on_fatal_error = 1; si = i + 2; -#if 0 - t = extract_command_subst (text, &si, SX_NOALLOC|SX_NOLONGJMP|SX_NOERROR|SX_COMPLETE); -#else t = extract_command_subst (text, &si, SX_NOALLOC|SX_NOLONGJMP|SX_NOERROR); -#endif i = si + 1; no_longjmp_on_fatal_error = o; if (i > tlen) diff --git a/lib/readline/callback.c b/lib/readline/callback.c index d78a7ca77..d376f948f 100644 --- a/lib/readline/callback.c +++ b/lib/readline/callback.c @@ -363,7 +363,7 @@ rl_callback_sigcleanup (void) if (RL_ISSTATE (RL_STATE_ISEARCH)) _rl_isearch_cleanup (_rl_iscxt, 0); else if (RL_ISSTATE (RL_STATE_NSEARCH)) - _rl_nsearch_cleanup (_rl_nscxt, 0); + _rl_nsearch_sigcleanup (_rl_nscxt, 0); else if (RL_ISSTATE (RL_STATE_VIMOTION)) RL_UNSETSTATE (RL_STATE_VIMOTION); else if (RL_ISSTATE (RL_STATE_NUMERICARG)) @@ -373,6 +373,9 @@ rl_callback_sigcleanup (void) } else if (RL_ISSTATE (RL_STATE_MULTIKEY)) RL_UNSETSTATE (RL_STATE_MULTIKEY); + else if (RL_ISSTATE (RL_STATE_READSTR)) + _rl_readstr_sigcleanup (_rl_rscxt, 0); + if (RL_ISSTATE (RL_STATE_CHARSEARCH)) RL_UNSETSTATE (RL_STATE_CHARSEARCH); diff --git a/lib/readline/readline.h b/lib/readline/readline.h index f8c356e50..3224706f7 100644 --- a/lib/readline/readline.h +++ b/lib/readline/readline.h @@ -914,37 +914,40 @@ extern int rl_persistent_signal_handlers; #define MULT_MATCH 2 /* Possible state values for rl_readline_state */ -#define RL_STATE_NONE 0x000000 /* no state; before first call */ - -#define RL_STATE_INITIALIZING 0x0000001 /* initializing */ -#define RL_STATE_INITIALIZED 0x0000002 /* initialization done */ -#define RL_STATE_TERMPREPPED 0x0000004 /* terminal is prepped */ -#define RL_STATE_READCMD 0x0000008 /* reading a command key */ -#define RL_STATE_METANEXT 0x0000010 /* reading input after ESC */ -#define RL_STATE_DISPATCHING 0x0000020 /* dispatching to a command */ -#define RL_STATE_MOREINPUT 0x0000040 /* reading more input in a command function */ -#define RL_STATE_ISEARCH 0x0000080 /* doing incremental search */ -#define RL_STATE_NSEARCH 0x0000100 /* doing non-inc search */ -#define RL_STATE_SEARCH 0x0000200 /* doing a history search */ -#define RL_STATE_NUMERICARG 0x0000400 /* reading numeric argument */ -#define RL_STATE_MACROINPUT 0x0000800 /* getting input from a macro */ -#define RL_STATE_MACRODEF 0x0001000 /* defining keyboard macro */ -#define RL_STATE_OVERWRITE 0x0002000 /* overwrite mode */ -#define RL_STATE_COMPLETING 0x0004000 /* doing completion */ -#define RL_STATE_SIGHANDLER 0x0008000 /* in readline sighandler */ -#define RL_STATE_UNDOING 0x0010000 /* doing an undo */ -#define RL_STATE_INPUTPENDING 0x0020000 /* rl_execute_next called */ -#define RL_STATE_TTYCSAVED 0x0040000 /* tty special chars saved */ -#define RL_STATE_CALLBACK 0x0080000 /* using the callback interface */ -#define RL_STATE_VIMOTION 0x0100000 /* reading vi motion arg */ -#define RL_STATE_MULTIKEY 0x0200000 /* reading multiple-key command */ -#define RL_STATE_VICMDONCE 0x0400000 /* entered vi command mode at least once */ -#define RL_STATE_CHARSEARCH 0x0800000 /* vi mode char search */ -#define RL_STATE_REDISPLAYING 0x1000000 /* updating terminal display */ - -#define RL_STATE_DONE 0x2000000 /* done; accepted line */ -#define RL_STATE_TIMEOUT 0x4000000 /* done; timed out */ -#define RL_STATE_EOF 0x8000000 /* done; got eof on read */ +#define RL_STATE_NONE 0x0000000 /* no state; before first call */ + +#define RL_STATE_INITIALIZING 0x00000001 /* initializing */ +#define RL_STATE_INITIALIZED 0x00000002 /* initialization done */ +#define RL_STATE_TERMPREPPED 0x00000004 /* terminal is prepped */ +#define RL_STATE_READCMD 0x00000008 /* reading a command key */ +#define RL_STATE_METANEXT 0x00000010 /* reading input after ESC */ +#define RL_STATE_DISPATCHING 0x00000020 /* dispatching to a command */ +#define RL_STATE_MOREINPUT 0x00000040 /* reading more input in a command function */ +#define RL_STATE_ISEARCH 0x00000080 /* doing incremental search */ +#define RL_STATE_NSEARCH 0x00000100 /* doing non-inc search */ +#define RL_STATE_SEARCH 0x00000200 /* doing a history search */ +#define RL_STATE_NUMERICARG 0x00000400 /* reading numeric argument */ +#define RL_STATE_MACROINPUT 0x00000800 /* getting input from a macro */ +#define RL_STATE_MACRODEF 0x00001000 /* defining keyboard macro */ +#define RL_STATE_OVERWRITE 0x00002000 /* overwrite mode */ +#define RL_STATE_COMPLETING 0x00004000 /* doing completion */ +#define RL_STATE_SIGHANDLER 0x00008000 /* in readline sighandler */ +#define RL_STATE_UNDOING 0x00010000 /* doing an undo */ +#define RL_STATE_INPUTPENDING 0x00020000 /* rl_execute_next called */ +#define RL_STATE_TTYCSAVED 0x00040000 /* tty special chars saved */ +#define RL_STATE_CALLBACK 0x00080000 /* using the callback interface */ +#define RL_STATE_VIMOTION 0x00100000 /* reading vi motion arg */ +#define RL_STATE_MULTIKEY 0x00200000 /* reading multiple-key command */ +#define RL_STATE_VICMDONCE 0x00400000 /* entered vi command mode at least once */ +#define RL_STATE_CHARSEARCH 0x00800000 /* vi mode char search */ +#define RL_STATE_REDISPLAYING 0x01000000 /* updating terminal display */ + +#define RL_STATE_DONE 0x02000000 /* done; accepted line */ +#define RL_STATE_TIMEOUT 0x04000000 /* done; timed out */ +#define RL_STATE_EOF 0x08000000 /* done; got eof on read */ + +/* Rearrange these for next major version */ +#define RL_STATE_READSTR 0x10000000 /* reading a string for M-x */ #define RL_SETSTATE(x) (rl_readline_state |= (x)) #define RL_UNSETSTATE(x) (rl_readline_state &= ~(x)) diff --git a/lib/readline/rlprivate.h b/lib/readline/rlprivate.h index 461617c4b..a2ab98bc1 100644 --- a/lib/readline/rlprivate.h +++ b/lib/readline/rlprivate.h @@ -67,6 +67,7 @@ #define SF_CHGKMAP 0x08 #define SF_PATTERN 0x10 #define SF_NOCASE 0x20 /* unused so far */ +#define SF_FREEPMT 0x40 /* saved prompt separately, need to free it */ typedef struct __rl_search_context { @@ -111,7 +112,8 @@ typedef struct __rl_search_context } _rl_search_cxt; /* readstr flags */ -#define RL_READSTR_NOSPACE 0x01 /* don't insert space, use for completion */ +#define READSTR_NOSPACE 0x01 /* don't insert space, use for completion */ +#define READSTR_FREEPMT 0x02 /* called rl_save_prompt, need to free it ourselves */ typedef struct __rl_readstr_context { @@ -432,6 +434,7 @@ extern int _rl_restore_tty_signals (void); /* search.c */ extern int _rl_nsearch_callback (_rl_search_cxt *); extern int _rl_nsearch_cleanup (_rl_search_cxt *, int); +extern int _rl_nsearch_sigcleanup (_rl_search_cxt *, int); extern void _rl_free_saved_search_line (void); @@ -443,6 +446,8 @@ extern void _rl_release_sigint (void); extern void _rl_block_sigwinch (void); extern void _rl_release_sigwinch (void); +extern void _rl_state_sigcleanup (void); + /* terminal.c */ extern void _rl_get_screen_size (int, int); extern void _rl_sigwinch_resize_terminal (void); @@ -488,6 +493,7 @@ extern void _rl_free_saved_readstr_line (void); extern void _rl_unsave_saved_readstr_line (void); extern _rl_readstr_cxt *_rl_readstr_init (int, int); extern int _rl_readstr_cleanup (_rl_readstr_cxt *, int); +extern int _rl_readstr_sigcleanup (_rl_readstr_cxt *, int); extern void _rl_readstr_restore (_rl_readstr_cxt *); extern int _rl_readstr_getchar (_rl_readstr_cxt *); extern int _rl_readstr_dispatch (_rl_readstr_cxt *, int); diff --git a/lib/readline/search.c b/lib/readline/search.c index 37db052ec..6b078d7c2 100644 --- a/lib/readline/search.c +++ b/lib/readline/search.c @@ -286,6 +286,7 @@ _rl_nsearch_init (int dir, int pchar) rl_end = rl_point = 0; p = _rl_make_prompt_for_search (pchar ? pchar : ':'); + cxt->sflags |= SF_FREEPMT; rl_message ("%s", p); xfree (p); @@ -313,13 +314,24 @@ _rl_nsearch_abort (_rl_search_cxt *cxt) _rl_unsave_saved_search_line (); rl_point = cxt->save_point; rl_mark = cxt->save_mark; - rl_restore_prompt (); + if (cxt->sflags & SF_FREEPMT) + rl_restore_prompt (); /* _rl_make_prompt_for_search saved it */ + cxt->sflags &= ~SF_FREEPMT; rl_clear_message (); _rl_fix_point (1); RL_UNSETSTATE (RL_STATE_NSEARCH); } +int +_rl_nsearch_sigcleanup (_rl_search_cxt *cxt, int r) +{ + if (cxt->sflags & SF_FREEPMT) + rl_restore_prompt (); /* _rl_make_prompt_for_search saved it */ + cxt->sflags &= ~SF_FREEPMT; + return (_rl_nsearch_cleanup (cxt, r)); +} + /* Process just-read character C according to search context CXT. Return -1 if the caller should abort the search, 0 if we should break out of the loop, and 1 if we should continue to read characters. */ @@ -427,7 +439,9 @@ _rl_nsearch_dosearch (_rl_search_cxt *cxt) { _rl_free_saved_search_line (); rl_ding (); - rl_restore_prompt (); + if (cxt->sflags & SF_FREEPMT) + rl_restore_prompt (); + cxt->sflags &= ~SF_FREEPMT; RL_UNSETSTATE (RL_STATE_NSEARCH); return -1; } @@ -452,7 +466,9 @@ _rl_nsearch_dosearch (_rl_search_cxt *cxt) #endif } - rl_restore_prompt (); + if (cxt->sflags & SF_FREEPMT) + rl_restore_prompt (); + cxt->sflags &= ~SF_FREEPMT; return (noninc_dosearch (noninc_search_string, cxt->direction, cxt->sflags&SF_PATTERN)); } diff --git a/lib/readline/signals.c b/lib/readline/signals.c index 51d746809..92dd2ffc9 100644 --- a/lib/readline/signals.c +++ b/lib/readline/signals.c @@ -585,6 +585,19 @@ rl_reset_after_signal (void) rl_set_signals (); } +/* Similar to rl_callback_sigcleanup, but cleans up operations that allocate + state even when not in callback mode. */ +void +_rl_state_sigcleanup (void) +{ + if (RL_ISSTATE (RL_STATE_ISEARCH)) /* incremental search */ + _rl_isearch_cleanup (_rl_iscxt, 0); + else if (RL_ISSTATE (RL_STATE_NSEARCH)) /* non-incremental search */ + _rl_nsearch_sigcleanup (_rl_nscxt, 0); + else if (RL_ISSTATE (RL_STATE_READSTR)) /* reading a string */ + _rl_readstr_sigcleanup (_rl_rscxt, 0); +} + /* Free up the readline variable line state for the current line (undo list, any partial history entry, any keyboard macros in progress, and any numeric arguments in process) after catching a signal, before calling @@ -594,6 +607,9 @@ rl_free_line_state (void) { register HIST_ENTRY *entry; + if (RL_ISSTATE (RL_STATE_CALLBACK) == 0) + _rl_state_sigcleanup (); + rl_free_undo_list (); entry = current_history (); diff --git a/lib/readline/text.c b/lib/readline/text.c index 25e848492..e8bf4506a 100644 --- a/lib/readline/text.c +++ b/lib/readline/text.c @@ -1971,10 +1971,13 @@ _rl_rscxt_dispose (_rl_readstr_cxt *cxt, int flags) xfree (cxt); } +/* This isn't used yet */ void _rl_free_saved_readstr_line () { if (_rl_saved_line_for_readstr) + /* This doesn't free any saved undo list, if it needs to, + rl_clear_history shows how to do it. */ _rl_free_saved_line (_rl_saved_line_for_readstr); _rl_saved_line_for_readstr = (HIST_ENTRY *)NULL; } @@ -1983,7 +1986,10 @@ void _rl_unsave_saved_readstr_line () { if (_rl_saved_line_for_readstr) - _rl_unsave_line (_rl_saved_line_for_readstr); + { + _rl_free_undo_list (rl_undo_list); + _rl_unsave_line (_rl_saved_line_for_readstr); /* restores rl_undo_list */ + } _rl_saved_line_for_readstr = (HIST_ENTRY *)NULL; } @@ -2004,9 +2010,12 @@ _rl_readstr_init (int pchar, int flags) rl_end = rl_point = 0; p = _rl_make_prompt_for_search (pchar ? pchar : '@'); + cxt->flags |= READSTR_FREEPMT; rl_message ("%s", p); xfree (p); + RL_SETSTATE (RL_STATE_READSTR); + _rl_rscxt = cxt; return cxt; @@ -2018,6 +2027,8 @@ _rl_readstr_cleanup (_rl_readstr_cxt *cxt, int r) _rl_rscxt_dispose (cxt, 0); _rl_rscxt = 0; + RL_UNSETSTATE (RL_STATE_READSTR); + return (r != 1); } @@ -2027,11 +2038,22 @@ _rl_readstr_restore (_rl_readstr_cxt *cxt) _rl_unsave_saved_readstr_line (); /* restores rl_undo_list */ rl_point = cxt->save_point; rl_mark = cxt->save_mark; - rl_restore_prompt (); /* _rl_make_prompt_for_search saved it */ + if (cxt->flags & READSTR_FREEPMT) + rl_restore_prompt (); /* _rl_make_prompt_for_search saved it */ + cxt->flags &= ~READSTR_FREEPMT; rl_clear_message (); _rl_fix_point (1); } +int +_rl_readstr_sigcleanup (_rl_readstr_cxt *cxt, int r) +{ + if (cxt->flags & READSTR_FREEPMT) + rl_restore_prompt (); /* _rl_make_prompt_for_search saved it */ + cxt->flags &= ~READSTR_FREEPMT; + return (_rl_readstr_cleanup (cxt, r)); +} + int _rl_readstr_getchar (_rl_readstr_cxt *cxt) { @@ -2130,7 +2152,7 @@ _rl_readstr_dispatch (_rl_readstr_cxt *cxt, int c) break; case ' ': - if ((cxt->flags & RL_READSTR_NOSPACE) == 0) + if ((cxt->flags & READSTR_NOSPACE) == 0) { _rl_insert_char (1, c); break; @@ -2273,7 +2295,7 @@ _rl_read_command_name () char *ret; int c, r; - cxt = _rl_readstr_init ('!', RL_READSTR_NOSPACE); + cxt = _rl_readstr_init ('!', READSTR_NOSPACE); cxt->compfunc = _rl_readcmd_complete; /* skip callback stuff for now */ @@ -2344,5 +2366,6 @@ rl_execute_named_command (int count, int key) r = 1; } + free (command); return r; }