subshells should not inherit FIFOs; initial framework for nofork (foreground) command substitutions

This commit is contained in:
Chet Ramey
2023-05-09 10:33:56 -04:00
parent 896df2874a
commit c375f8f45f
11 changed files with 341 additions and 34 deletions
+45
View File
@@ -6257,3 +6257,48 @@ eval.c
setting it to 0 for the expansion so it doesn't affect things like
command substitution.
From a report by Grisha Levit <grishalevit@gmail.com>
parse.y
- parse_comsub: handle ${Ccommand; }, where C == space, tab, newline,
or '|'
- read_token_word: understand ${Ccommand; } and call parse_comsub to
parse the contents
- parse_matched_pair: understand ${Ccommand; } inside another paired
construct
- new `funsub' production, like comsub but for ${Ccommand;} nofork
command substitution
execute_cmd.c
- execute_simple_command: if we fork because of pipes in or out, clear
the fifo list, since subshell aren't supposed to inherit fifos
5/8
---
parser.h
- PST_FUNSUBST: new parser state, parsing the command in ${Ccommand;}.
Primarily used to have read_token_word return `}' as a separate
token even if it's not delimited like a reserved word
subst.h
- SX_FUNSUB: new flag, tell xparse_dolparen we are parsing the command
in a ${Ccommand;} nofork command substitution
parse.y
- xparse_dolparen: handle parsing ${Ccommand;} nofork command
substitution like parse_comsub
- read_token: if we're parsing a foreground command substitution,
treat a word that begins with an unquoted `}' as a `}' token to
terminate the ${Ccommand;} construct without requiring that the
close brace be delimited like a reserved word
- no_semi_successors: add DOLBRACE
- reserved_word_acceptable: add DOLBRACE
subst.c
- extract_function_subst: like extract_command_subst, but for nofork
command substitutions; calls xparse_dolparen with the SX_FUNSUB
flag to differentiate
- string_extract_double_quoted,extract_delimited_string,
extract_heredoc_dolbrace_string,extract_dollar_brace_string,
param_expand: call extract_function_subst as appropriate
- function_substitute: like command_substitute, but for nofork command
substitutions. Just a stub for now
+2 -1
View File
@@ -811,7 +811,8 @@ $(srcdir)/configure: $(srcdir)/configure.ac $(srcdir)/aclocal.m4 $(srcdir)/confi
reconfig: force
sh $(srcdir)/configure -C
loadables:
# force loadables to wait until the shell is built
loadables: .made
cd $(LOADABLES_DIR) && $(MAKE) $(MFLAGS) all
#newversion: mkversion
+1 -1
View File
@@ -9539,7 +9539,7 @@ in the same way as \fBecho \-e\fP.
.B %q
causes \fBprintf\fP to output the corresponding
\fIargument\fP in a format that can be reused as shell input.
\fB%q\fP and \fB%Q\fP use the \fB$''\fP quoting style if any characters
\fB%q\fP and \fB%Q\fP use the \fB$\(aq\(aq\fP quoting style if any characters
in the argument string require it, and backslash quoting otherwise.
If the format string uses the \fIprintf\fP alternate form, these two
formats quote the argument string using single quotes.
+2 -2
View File
@@ -475,8 +475,8 @@ when in double quotes (@pxref{Shell Parameter Expansion}).
@subsubsection ANSI-C Quoting
@cindex quoting, ANSI
Character sequences of the form $'@var{string}' are treated as a special
kind of single quotes.
Character sequences of the form @code{$'@var{string}'} are treated as
a special kind of single quotes.
The sequence expands to @var{string}, with backslash-escaped characters
in @var{string} replaced as specified by the ANSI C standard.
Backslash escape sequences, if present, are decoded as follows:
+30 -1
View File
@@ -4430,6 +4430,10 @@ execute_simple_command (SIMPLE_COM *simple_command, int pipe_in, int pipe_out, i
coproc_closeall ();
#endif
#if defined (PROCESS_SUBSTITUTION)
clear_fifo_list (); /* subshells don't inherit fifos */
#endif
last_asynchronous_pid = old_last_async_pid;
if (async)
@@ -4767,13 +4771,38 @@ execute_from_filesystem:
/* The old code did not test already_forked and only did this if
subshell_environment&SUBSHELL_COMSUB != 0 (comsubs and procsubs). Other
uses of the no-fork optimization left FIFOs in $TMPDIR */
if (already_forked == 0 && (cmdflags & CMD_NO_FORK) && fifos_pending() > 0)
if (already_forked == 0 && (cmdflags & CMD_NO_FORK) && fifos_pending () > 0)
cmdflags &= ~CMD_NO_FORK;
if (dofork && already_forked && (subshell_environment & SUBSHELL_PIPE) &&
(cmdflags & CMD_NO_FORK) && fifos_pending () > 0)
#if 0
cmdflags &= ~CMD_NO_FORK;
#else
; /* can't turn off nofork here, too many processes have the FIFOs open */
#endif
#endif
result = execute_disk_command (words, simple_command->redirects, command_line,
pipe_in, pipe_out, async, fds_to_close,
cmdflags);
#if 0
/* If we forked but still have to fork again to run the disk command, we
did so because we created FIFOs. We can't just execve the command in case
it dies of a signal without a chance to clean up the FIFOs, so we fork
again, then make sure we wait for the child from execute_disk_command(),
unlink the FIFOs we created, and exit ourselves. */
if (dofork && already_forked && (cmdflags & CMD_NO_FORK) == 0)
{
result = wait_for (last_made_pid, 0);
#if defined (PROCESS_SUBSTITUTION)
if (fifos_pending ())
unlink_fifo_list ();
#endif
sh_exit (result & 0xFF);
}
#endif
return_result:
bind_lastarg (lastarg);
FREE (command_line);
+115 -24
View File
@@ -394,7 +394,7 @@ static FILE *yyerrstream;
%type <command> cond_command
%type <command> arith_for_command
%type <command> coproc
%type <command> comsub
%type <command> comsub funsub
%type <command> function_def function_body if_command elif_clause subshell
%type <redirect> redirection redirection_list
%type <element> simple_command_element
@@ -429,6 +429,14 @@ inputunit: simple_list simple_list_terminator
eof_encountered = 0;
YYACCEPT;
}
| funsub
{
/* This is special; look at the production and how
parse_comsub/parse_valsub sets token_to_read */
global_command = $1;
eof_encountered = 0;
YYACCEPT;
}
| '\n'
{
/* Case of regular command, but not a very
@@ -1043,7 +1051,9 @@ comsub: DOLPAREN compound_list ')'
{
$$ = (COMMAND *)NULL;
}
| DOLBRACE compound_list '}'
;
funsub: DOLBRACE compound_list '}'
{
$$ = $2;
}
@@ -3983,7 +3993,16 @@ parse_dollar_word:
if (ch == '(') /* ) */
nestret = parse_comsub (0, '(', ')', &nestlen, (rflags|P_COMMAND) & ~P_DQUOTE);
else if (ch == '{') /* } */
nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|P_DOLBRACE|rflags);
{
int npeek;
npeek = shell_getc (1);
shell_ungetc (npeek);
if (FUNSUB_CHAR (npeek))
nestret = parse_comsub (0, '{', '}', &nestlen, rflags|P_COMMAND);
else
nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|P_DOLBRACE|rflags);
}
else if (ch == '[') /* ] */
nestret = parse_matched_pair (0, '[', ']', &nestlen, rflags|P_ARITH);
@@ -4141,6 +4160,11 @@ dump_pflags (int flags)
f &= ~PST_CMDSUBST;
fprintf (stderr, "PST_CMDSUBST%s", f ? "|" : "");
}
if (f & PST_FUNSUBST)
{
f &= ~PST_FUNSUBST;
fprintf (stderr, "PST_FUNSUBST%s", f ? "|" : "");
}
if (f & PST_CASESTMT)
{
f &= ~PST_CASESTMT;
@@ -4248,7 +4272,7 @@ static char *
parse_comsub (int qc, int open, int close, size_t *lenp, int flags)
{
int peekc, r;
int start_lineno, local_extglob, was_extpat;
int start_lineno, dolbrace_spec, local_extglob, was_extpat;
char *ret, *tcmd;
size_t retlen;
sh_parser_state_t ps;
@@ -4261,9 +4285,20 @@ parse_comsub (int qc, int open, int close, size_t *lenp, int flags)
{
peekc = shell_getc (1);
shell_ungetc (peekc);
if (peekc == '(') /*)*/
if (peekc == '(') /* ) */
return (parse_matched_pair (qc, open, close, lenp, P_ARITH));
}
else if (open == '{') /* } */
{
peekc = shell_getc (1);
if (FUNSUB_CHAR (peekc))
dolbrace_spec = peekc;
else
{
shell_ungetc (peekc);
return parse_matched_pair (qc, open, close, lenp, flags);
}
}
/*itrace("parse_comsub: qc = `%c' open = %c close = %c", qc, open, close);*/
@@ -4283,6 +4318,8 @@ parse_comsub (int qc, int open, int close, size_t *lenp, int flags)
parser_state &= ~(PST_CASEPAT|PST_ALEXPNEXT|PST_SUBSHELL|PST_REDIRLIST);
/* State flags we want to set for this run through the parser. */
parser_state |= PST_CMDSUBST|PST_EOFTOKEN|PST_NOEXPAND;
if (open == '{') /* } */
parser_state |= PST_FUNSUBST;
/* leave pushed_string_list alone, since we might need to consume characters
from it to satisfy this command substitution (in some perverse case). */
@@ -4312,7 +4349,7 @@ parse_comsub (int qc, int open, int close, size_t *lenp, int flags)
#endif
current_token = '\n'; /* XXX */
token_to_read = DOLPAREN; /* let's trick the parser */
token_to_read = (open == '(') ? DOLPAREN : DOLBRACE; /* let's trick the parser ) */
r = yyparse ();
@@ -4381,17 +4418,33 @@ INTERNAL_DEBUG(("current_token (%d) != shell_eof_token (%c)", current_token, she
tcmd = print_comsub (parsed_command); /* returns static memory */
retlen = strlen (tcmd);
if (tcmd[0] == '(') /* ) need a space to prevent arithmetic expansion */
retlen++;
ret = xmalloc (retlen + 2);
if (tcmd[0] == '(') /* ) */
if (open == '(') /* ) */
{
ret[0] = ' ';
strcpy (ret + 1, tcmd);
if (tcmd[0] == '(') /* ) need a space to prevent arithmetic expansion */
retlen++;
ret = xmalloc (retlen + 2);
if (tcmd[0] == '(') /* ) */
{
ret[0] = ' ';
strcpy (ret + 1, tcmd);
}
else
strcpy (ret, tcmd);
}
else
strcpy (ret, tcmd);
ret[retlen++] = ')';
else /* open == '{' } */
{
int lastc;
lastc = tcmd[retlen - 1];
retlen++;
ret = xmalloc (retlen + 4);
ret[0] = (dolbrace_spec == '|') ? '|' : ' ';
strcpy (ret + 1, tcmd); /* ( */
if (lastc != '\n' && lastc != ';' && lastc != '&' && lastc != ')')
ret[retlen++] = ';';
ret[retlen++] = ' ';
}
ret[retlen++] = close;
ret[retlen] = '\0';
dispose_command (parsed_command);
@@ -4412,7 +4465,7 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
{
sh_parser_state_t ps;
sh_input_line_state_t ls;
int orig_ind, nc, sflags, start_lineno, local_extglob;
int orig_ind, nc, sflags, start_lineno, local_extglob, funsub, closer;
char *ret, *ep, *ostring;
/*debug_parser(1);*/
@@ -4432,6 +4485,8 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
/*itrace("xparse_dolparen: size = %d shell_input_line = `%s' string=`%s'", shell_input_line_size, shell_input_line, string);*/
funsub = flags & SX_FUNSUB;
sflags = SEVAL_NONINT|SEVAL_NOHIST|SEVAL_NOFREE;
if (flags & SX_NOLONGJMP)
sflags |= SEVAL_NOLONGJMP;
@@ -4443,10 +4498,12 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
pushed_string_list = (STRING_SAVER *)NULL;
#endif
/*(*/
parser_state |= PST_CMDSUBST|PST_EOFTOKEN; /* allow instant ')' */ /*(*/
shell_eof_token = ')';
parser_state |= PST_CMDSUBST|PST_EOFTOKEN; /* allow instant ')' */ /*{(*/
closer = shell_eof_token = funsub ? '}' : ')';
if (flags & SX_COMPLETE)
parser_state |= PST_NOERROR;
if (funsub)
parser_state |= PST_FUNSUBST;
/* Don't expand aliases on this pass at all. Either parse_comsub() does it
at parse time, in which case this string already has aliases expanded,
@@ -4458,7 +4515,10 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
local_extglob = extended_glob;
#endif
token_to_read = DOLPAREN; /* let's trick the parser */
if (funsub && FUNSUB_CHAR (*string))
string++;
token_to_read = funsub ? DOLBRACE : DOLPAREN; /* let's trick the parser */
nc = parse_string (string, "command substitution", sflags, (COMMAND **)NULL, &ep);
@@ -4485,7 +4545,7 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
{
clear_shell_input_line (); /* XXX */
if (bash_input.type != st_string) /* paranoia */
parser_state &= ~(PST_CMDSUBST|PST_EOFTOKEN);
parser_state &= ~(PST_CMDSUBST|PST_EOFTOKEN|PST_FUNSUBST);
if ((flags & SX_NOLONGJMP) == 0)
jump_to_top_level (-nc); /* XXX */
}
@@ -4495,7 +4555,7 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
and return it. If flags & 1 (SX_NOALLOC) we can return NULL. */
/*(*/
if (ep[-1] != ')')
if (ep[-1] != closer)
{
#if 0
if (ep[-1] != '\n')
@@ -4516,7 +4576,7 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
itrace("xparse_dolparen:%d: *indp (%d) < orig_ind (%d), orig_string = `%s'", line_number, *indp, orig_ind, ostring);
#endif
if (base[*indp] != ')' && (flags & SX_NOLONGJMP) == 0)
if (base[*indp] != closer && (flags & SX_NOLONGJMP) == 0)
{
/*(*/
if ((flags & SX_NOERROR) == 0)
@@ -5096,6 +5156,24 @@ read_token_word (int character)
}
}
/* Just an awful special case. We want '}' to delimit the foreground
command substitution construct ${Ccommand; } but since it's a word
expansion, we need to join it with any potential following
characters. We fake things out here and treat a word beginning with
a close brace as the '}' reserved word, treat it as a separate
token, terminate the command substitution, and go on reading
characters into the same upper-layer token. */
if ((parser_state & PST_FUNSUBST) && token_index == 0 && quoted == 0 &&
reserved_word_acceptable (last_read_token) &&
MBTEST(character == '}'))
{
RESIZE_MALLOCED_BUFFER (token, token_index, 2,
token_buffer_size, TOKEN_DEFAULT_GROW_SIZE);
token[token_index++] = character;
all_digit_token = dollar_present = 0;
goto got_token;
}
/* Parse a matched pair of quote characters. */
if MBTEST(shellquote (character))
{
@@ -5180,7 +5258,19 @@ read_token_word (int character)
((peek_char == '{' || peek_char == '[') && character == '$')) /* ) ] } */
{
if (peek_char == '{') /* } */
ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE|P_DOLBRACE);
{
int npeek;
npeek = shell_getc (1);
shell_ungetc (npeek);
if (FUNSUB_CHAR (npeek))
{
push_delimiter (dstack, peek_char);
ttok = parse_comsub (cd, '{', '}', &ttoklen, P_COMMAND);
pop_delimiter (dstack);
}
else
ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE|P_DOLBRACE);
}
else if (peek_char == '(') /* ) */
{
/* XXX - push and pop the `(' as a delimiter for use by
@@ -5587,6 +5677,7 @@ reserved_word_acceptable (int toksym)
case WHILE:
case 0:
case DOLPAREN:
case DOLBRACE:
return 1;
default:
#if defined (COPROCESS_SUPPORT)
@@ -5655,7 +5746,7 @@ reset_readline_prompt (void)
static const int no_semi_successors[] = {
'\n', '{', '(', ')', ';', '&', '|',
CASE, DO, ELSE, IF, SEMI_SEMI, SEMI_AND, SEMI_SEMI_AND, THEN, UNTIL,
WHILE, AND_AND, OR_OR, IN, DOLPAREN,
WHILE, AND_AND, OR_OR, IN, DOLPAREN, DOLBRACE,
0
};
+5
View File
@@ -52,6 +52,7 @@
#define PST_NOERROR 0x800000 /* don't print error messages in yyerror */
#define PST_STRING 0x1000000 /* parsing a string to a command or word list */
#define PST_CMDBLTIN 0x2000000 /* last token was the `command' builtin */
#define PST_FUNSUBST 0x4000000 /* parsing a foreground command substitution */
/* Definition of the delimiter stack. Needed by parse.y and bashhist.c. */
struct dstack {
@@ -75,6 +76,10 @@ struct dstack {
#define DOLBRACE_QUOTE 0x40 /* single quote is special in double quotes */
#define DOLBRACE_QUOTE2 0x80 /* single quote is semi-special in double quotes */
/* characters that can appear following ${ to introduce a function or value
substitution (this is mksh terminology and needs to be changed). */
#define FUNSUB_CHAR(n) ((n) == ' ' || (n) == '\t' || (n) == '\n' || (n) == '|')
/* variable declarations from parse.y */
extern struct dstack dstack;
+126 -4
View File
@@ -934,6 +934,8 @@ add_one_character:
si = i + 2;
if (string[i + 1] == LPAREN)
ret = extract_command_subst (string, &si, (flags & SX_COMPLETE));
else if (string[i + 1] == LBRACE && FUNSUB_CHAR (string[si]))
ret = extract_function_subst (string, &si, Q_DOUBLE_QUOTES, (flags & SX_COMPLETE));
else
ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, 0);
@@ -1036,6 +1038,8 @@ skip_double_quoted (const char *string, size_t slen, int sind, int flags)
si = i + 2;
if (string[i + 1] == LPAREN)
ret = extract_command_subst (string, &si, SX_NOALLOC|(flags&SX_COMPLETE));
else if (string[i + 1] == LBRACE && FUNSUB_CHAR (string[si]))
ret = extract_function_subst (string, &si, Q_DOUBLE_QUOTES, (flags & SX_COMPLETE));
else
ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, SX_NOALLOC);
@@ -1256,6 +1260,25 @@ extract_command_subst (const char *string, int *sindex, int xflags)
}
}
/* Take a ${Ccommand} where C is a character that introduces a function
substitution and extract the string. */
char *
extract_function_subst (const char *string, int *sindex, int quoted, int xflags)
{
char *ret;
char *xstr;
if (string[*sindex] == LBRACE || (xflags & SX_COMPLETE))
return (extract_dollar_brace_string (string, sindex, quoted, xflags|SX_COMMAND));
else
{
xflags |= (no_longjmp_on_fatal_error ? SX_NOLONGJMP : 0);
xstr = (char *)string + *sindex;
ret = xparse_dolparen (string, xstr, sindex, xflags|SX_FUNSUB);
return ret;
}
}
/* Extract the $[ construct in STRING, and return a new string. (])
Start extracting at (SINDEX) as if we had just seen "$[".
Make (SINDEX) get the position of the matching "]". */
@@ -1392,6 +1415,16 @@ extract_delimited_string (const char *string, int *sindex, char *opener, char *a
continue;
}
/* Process alternate form of nested command substitution. */
if ((flags & SX_COMMAND) && string[i] == '$' && string[i+1] == LBRACE && FUNSUB_CHAR (string[i+2]))
{
si = i + 2;
t = extract_function_subst (string, &si, 0, flags|SX_NOALLOC);
CHECK_STRING_OVERRUN (i, si, slen, c);
i = si + 1;
continue;
}
/* Process a nested OPENER. */
if (STREQN (string + i, opener, len_opener))
{
@@ -1641,16 +1674,22 @@ extract_heredoc_dolbrace_string (const char *string, int *sindex, int quoted, in
/* Pass the contents of new-style command substitutions and
arithmetic substitutions through verbatim. */
if (string[i] == '$' && string[i+1] == LPAREN)
if (string[i] == '$' && (string[i+1] == LPAREN || (string[i+1] == LBRACE && FUNSUB_CHAR (string[i+2]))))
{
int open;
si = i + 2;
t = extract_command_subst (string, &si, flags);
open = string[i+1];
if (open == LPAREN)
t = extract_command_subst (string, &si, flags);
else
t = extract_function_subst (string, &si, quoted, flags);
CHECK_STRING_OVERRUN (i, si, slen, c);
tlen = si - i - 2;
RESIZE_MALLOCED_BUFFER (result, result_index, tlen + 4, result_size, 64);
result[result_index++] = c;
result[result_index++] = LPAREN;
result[result_index++] = open;
strncpy (result + result_index, t, tlen);
result_index += tlen;
result[result_index++] = string[si];
@@ -1854,6 +1893,19 @@ extract_dollar_brace_string (const char *string, int *sindex, int quoted, int fl
continue;
}
/* Pass the contents of foreground command substitutions (funsub/valsub)
through verbatim. */
if (string[i] == '$' && string[i+1] == LBRACE && FUNSUB_CHAR (string[i+2]))
{
si = i + 2;
t = extract_function_subst (string, &si, quoted, flags|SX_NOALLOC);
CHECK_STRING_OVERRUN (i, si, slen, c);
i = si + 1;
continue;
}
#if defined (PROCESS_SUBSTITUTION)
/* Technically this should only work at the start of a word */
if ((string[i] == '<' || string[i] == '>') && string[i+1] == LPAREN)
@@ -2084,7 +2136,7 @@ skip_matched_pair (const char *string, int start, int open, int close, int flags
if (string[si] == '\0')
CQ_RETURN(si);
/* XXX - extract_command_subst here? */
/* XXX - extract_command_subst/extract_function_subst here? */
if (string[i+1] == LPAREN)
temp = extract_delimited_string (string, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */
else
@@ -6693,6 +6745,52 @@ read_comsub (int fd, int quoted, int flags, int *rflag)
return istring;
}
WORD_DESC *
function_substitute (char *string, int quoted, int flags)
{
pid_t old_pipeline_pgrp;
char *istring, *s;
int fd;
int result, fildes[2], function_value, pflags, rc, tflag, fork_flags;
int valsub;
WORD_DESC *ret;
sigset_t set, oset;
istring = (char *)NULL;
/* In the case of no command to run, just return NULL. */
for (s = string; s && *s && (shellblank (*s) || *s == '\n'); s++)
;
if (s == 0 || *s == 0)
return ((WORD_DESC *)NULL);
if (valsub = (*string == '|'))
string++;
/* Flags to pass to parse_and_execute() */
pflags = (interactive && sourcelevel == 0) ? SEVAL_RESETLINE : 0;
#if defined (JOB_CONTROL)
old_pipeline_pgrp = pipeline_pgrp;
/* Don't reset the pipeline pgrp if we're already a subshell in a pipeline or
we've already forked to run a disk command (and are expanding redirections,
for example). */
if ((subshell_environment & (SUBSHELL_FORK|SUBSHELL_PIPE)) == 0)
pipeline_pgrp = shell_pgrp;
save_pipeline (1);
stop_making_children ();
#endif /* JOB_CONTROL */
last_command_exit_value = EXECUTION_FAILURE;
report_error ("bad substitution: %s", string);
pipeline_pgrp = old_pipeline_pgrp;
restore_pipeline (1);
/* exp_jump_to_top_level (DISCARD); */
return ((WORD_DESC *)NULL);
}
/* Perform command substitution on STRING. This returns a WORD_DESC * with the
contained string possibly quoted. */
WORD_DESC *
@@ -10277,6 +10375,30 @@ param_expand (char *string, int *sindex, int quoted,
break;
case LBRACE:
/* Foreground command substitution. */
if (FUNSUB_CHAR (string[zindex + 1]))
{
/* We have to extract the contents of this command substitution. */
t_index = zindex + 1;
temp = extract_function_subst (string, &t_index, quoted, (pflags&PF_COMPLETE) ? SX_COMPLETE : 0);
zindex = t_index;
/* This is basically the same as the comsub code. */
if (pflags & PF_NOCOMSUB)
/* we need zindex+1 because string[zindex] == RPAREN */
temp1 = substring (string, *sindex, zindex+1);
else
{
tdesc = function_substitute (temp, quoted, pflags&PF_ASSIGNRHS);
temp1 = tdesc ? tdesc->word : (char *)NULL;
if (tdesc)
dispose_word_desc (tdesc);
}
FREE (temp);
temp = temp1;
break;
}
tdesc = parameter_brace_expand (string, &zindex, quoted, pflags,
quoted_dollar_at_p,
contains_dollar_at);
+5 -1
View File
@@ -66,7 +66,7 @@
#define SX_NOCTLESC 0x0010 /* don't honor CTLESC quoting */
#define SX_NOESCCTLNUL 0x0020 /* don't let CTLESC quote CTLNUL */
#define SX_NOLONGJMP 0x0040 /* don't longjmp on fatal error */
#define SX_ARITHSUB 0x0080 /* extracting $(( ... )) (currently unused) */
#define SX_FUNSUB 0x0080 /* extracting ${ command; }; passed to xparse_dolparen */
#define SX_POSIXEXP 0x0100 /* extracting new Posix pattern removal expansions in extract_dollar_brace_string */
#define SX_WORD 0x0200 /* extracting word in ${param op word} */
#define SX_COMPLETE 0x0400 /* extracting word for completion */
@@ -86,6 +86,9 @@ extern void unquote_bang (char *);
XFLAGS is additional flags to pass to other extraction functions, */
extern char *extract_command_subst (const char *, int *, int);
/* Placeholder */
extern char *extract_function_subst (const char *, int *, int, int);
/* Extract the $[ construct in STRING, and return a new string.
Start extracting at (SINDEX) as if we had just seen "$[".
Make (SINDEX) get the position just after the matching "]". */
@@ -274,6 +277,7 @@ extern WORD_LIST *expand_words_no_vars (WORD_LIST *);
extern WORD_LIST *expand_words_shellexp (WORD_LIST *);
extern WORD_DESC *command_substitute (char *, int, int);
extern WORD_DESC *function_substitute (char *, int, int);
extern char *pat_subst (char *, char *, char *, int);
#if defined (PROCESS_SUBSTITUTION)
+8
View File
@@ -0,0 +1,8 @@
FROM alpine:3.17
RUN apk add --no-cache bison coreutils gcc libc-dev make
ncurses-dev libncurses5-dev libncursesw5-dev
WORKDIR /tmp/bash
COPY . .
RUN ./configure --enable-readline --with-curses
RUN make
RUN make tests
+2
View File
@@ -78,6 +78,8 @@ env -i BASH_FUNC_x%%='() { _; } >_[${ $() }] { id; }' ${THIS_SH} -c : 2>/dev/nul
env BASH_FUNC_x%%=$'() { _;}>_[$($())]\n{ echo vuln;}' ${THIS_SH} -c : 2>/dev/null
eval 'x() { _;}>_[$($())] { echo vuln;}' 2>/dev/null
echo this will fail now that '${ ' has syntactic meaning
eval 'foo() { _; } >_[${ $() }] ;{ echo eval ok; }'
# other tests fixed in bash43-030 concerning function name transformation