- 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
+
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 == '$') /*)*/
{
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);
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;
#if defined (SHELL)
# include "shell.h"
+# include "parser.h" /* FUNSUB_CHAR */
#else
# if defined (TEST)
typedef char *WORD_DESC;
#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
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)
#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;
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)
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))
}
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);
#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))
#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
{
} _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
{
/* 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);
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);
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);
rl_end = rl_point = 0;
p = _rl_make_prompt_for_search (pchar ? pchar : ':');
+ cxt->sflags |= SF_FREEPMT;
rl_message ("%s", p);
xfree (p);
_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. */
{
_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;
}
#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));
}
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
{
register HIST_ENTRY *entry;
+ if (RL_ISSTATE (RL_STATE_CALLBACK) == 0)
+ _rl_state_sigcleanup ();
+
rl_free_undo_list ();
entry = current_history ();
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;
}
_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;
}
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;
_rl_rscxt_dispose (cxt, 0);
_rl_rscxt = 0;
+ RL_UNSETSTATE (RL_STATE_READSTR);
+
return (r != 1);
}
_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)
{
break;
case ' ':
- if ((cxt->flags & RL_READSTR_NOSPACE) == 0)
+ if ((cxt->flags & READSTR_NOSPACE) == 0)
{
_rl_insert_char (1, c);
break;
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 */
r = 1;
}
+ free (command);
return r;
}