make completion of nofork comsubs a little better; fix leaks on signals for non-incremental searches; fix leaks in readstr interface

This commit is contained in:
Chet Ramey
2024-01-27 16:28:31 -05:00
parent 10702735a0
commit 138f3cc359
9 changed files with 201 additions and 66 deletions
+65
View File
@@ -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 <the.true.nathan.mills@gmail.com>
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
+7 -2
View File
@@ -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;
+24 -26
View File
@@ -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)
+4 -1
View File
@@ -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);
+32 -29
View File
@@ -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_NONE 0x0000000 /* 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_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 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_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))
+7 -1
View File
@@ -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);
+19 -3
View File
@@ -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));
}
+16
View File
@@ -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 ();
+27 -4
View File
@@ -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;
}