fix for command substitution string parsing that resulted in top-level unwind-protects being run in the wrong spot; fix for delimiter byte being part of invalid multibyte character in read builtin

This commit is contained in:
Chet Ramey
2025-05-04 17:21:25 -04:00
parent 15df599354
commit 535a8150b6
11 changed files with 142 additions and 74 deletions
+24
View File
@@ -11186,3 +11186,27 @@ parse.y
newline, among other things.
From https://savannah.gnu.org/patch/?10517
4/29
----
builtins/evalstring.c
- parse_string: only run the top-level unwind-protects if we're
actually going to be calling jump_to_top_level(); otherwise let
xparse_dolparen take care of it
From a report by Александр Ушаков <aushakov@astralinux.ru>
5/1
---
lib/sh/zread.c
- zungetc: rework to use a local 16-byte buffer so we can push back
multiple characters whether we use read or zread
- zread/zreadretry/zreadintr/zreadc/zreadcintr/zreadn: changed to use
zpopbuf to check whether we have pushed back bytes
- zreset,zsyncfd: reset the pushback buffer indices
builtins/read.def
- read_mbchar: handle the delimiter being part of an invalid multibyte
character, not just the byte that makes the multibyte character
invalid: keep track of where the delimiter char is in the buffer and
use zungetc to push back that character and everything we read
after it so subsequent buffered (zread) or unbuffered (read) reads
will return them
+4 -3
View File
@@ -725,7 +725,8 @@ parse_string (char *string, const char *from_file, int flags, COMMAND **cmdp, ch
if (current_token == yacc_EOF || current_token == shell_eof_token)
{
if (current_token == shell_eof_token)
/* check for EOFTOKEN out of paranoia */
if ((parser_state & PST_EOFTOKEN) && (current_token == shell_eof_token))
rewind_input_string ();
break;
}
@@ -744,10 +745,10 @@ out:
us, after doing cleanup */
if (should_jump_to_top_level)
{
if (parse_and_execute_level == 0)
top_level_cleanup ();
if (code == DISCARD)
return -DISCARD;
if (parse_and_execute_level == 0)
top_level_cleanup ();
jump_to_top_level (code);
}
+15 -9
View File
@@ -1159,7 +1159,7 @@ static int
read_mbchar (int fd, char *string, int ind, int ch, int delim, int unbuffered)
{
char mbchar[MB_LEN_MAX + 1];
int i, n, r;
int i, n, r, delim_ind;
char c;
size_t ret;
mbstate_t ps, ps_back;
@@ -1167,7 +1167,8 @@ read_mbchar (int fd, char *string, int ind, int ch, int delim, int unbuffered)
memset (&ps, '\0', sizeof (mbstate_t));
memset (&ps_back, '\0', sizeof (mbstate_t));
delim_ind = 0;
mbchar[0] = ch;
i = 1;
for (n = 0; n <= MB_LEN_MAX; n++)
@@ -1187,19 +1188,24 @@ read_mbchar (int fd, char *string, int ind, int ch, int delim, int unbuffered)
r = zreadc (fd, &c);
if (r <= 0)
goto mbchar_return;
if ((unsigned char)c == delim)
delim_ind = i;
mbchar[i++] = c;
continue;
}
else if (ret == (size_t)-1)
{
/* If we read (i > 1) a delimiter character (c == delimiter)
that makes this an invalid multibyte character, we can't just
add it to the input string and treat it as a byte.
We need to push it back so a subsequent zread will pick it up. */
if (i > 1 && (unsigned char)c == delim)
/* If we read (i > 1) a delimiter character (delim_ind >= 1)
that is a part of this invalid multibyte character, we can't
just add it to the input string and treat it as a byte.
We need to push it and everything we read after it back so a
subsequent zread will pick it up. */
if (i > 1 && delim_ind >= 1)
{
zungetc ((unsigned char)c);
i--;
size_t j;
for (j = delim_ind; j < i; j++)
zungetc ((unsigned char)mbchar[j]);
i = delim_ind;
}
break; /* invalid multibyte character */
}
+3
View File
@@ -880,6 +880,9 @@
/* Define if you have the snprintf function. */
#undef HAVE_SNPRINTF
/* Define if you have the statfs function. */
#undef HAVE_STATFS
/* Define if you have the strcasecmp function. */
#undef HAVE_STRCASECMP
Vendored
+7 -1
View File
@@ -1,5 +1,5 @@
#! /bin/sh
# From configure.ac for Bash 5.3, version 5.077.
# From configure.ac for Bash 5.3, version 5.078.
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.72 for bash 5.3-rc1.
#
@@ -15581,6 +15581,12 @@ if test "x$ac_cv_func_setitimer" = xyes
then :
printf "%s\n" "#define HAVE_SETITIMER 1" >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "statfs" "ac_cv_func_statfs"
if test "x$ac_cv_func_statfs" = xyes
then :
printf "%s\n" "#define HAVE_STATFS 1" >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "tcgetpgrp" "ac_cv_func_tcgetpgrp"
if test "x$ac_cv_func_tcgetpgrp" = xyes
+3 -2
View File
@@ -21,7 +21,7 @@ dnl Process this file with autoconf to produce a configure script.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
AC_REVISION([for Bash 5.3, version 5.077])dnl
AC_REVISION([for Bash 5.3, version 5.078])dnl
define(bashvers, 5.3)
define(relstatus, rc1)
@@ -874,7 +874,8 @@ AC_CHECK_FUNCS(dup2 eaccess fcntl getdtablesize getentropy getgroups \
gethostname getpagesize getpeername getrandom getrlimit \
getrusage gettimeofday kill killpg lstat nanosleep \
pselect readlink \
select setdtablesize setitimer tcgetpgrp uname ulimit waitpid)
select setdtablesize setitimer statfs \
tcgetpgrp uname ulimit waitpid)
AC_REPLACE_FUNCS(rename)
dnl checks for c library functions
+5 -5
View File
@@ -770,11 +770,11 @@ PPAARRAAMMEETTEERRSS
value. The current value is usually an integer constant, but may be an
expression. When "+=" is applied to an array variable using compound
assignment (see AArrrraayyss below), the variable's value is not unset (as it
is when using and new values are appended to the array beginning at one
greater than the array's maximum index (for indexed arrays) or added as
additional key-value pairs in an associative array. When applied to a
string-valued variable, _v_a_l_u_e is expanded and appended to the vari-
able's value.
is when using "="), and new values are appended to the array beginning
at one greater than the array's maximum index (for indexed arrays) or
added as additional key-value pairs in an associative array. When ap-
plied to a string-valued variable, _v_a_l_u_e is expanded and appended to
the variable's value.
A variable can be assigned the _n_a_m_e_r_e_f attribute using the --nn option to
the ddeeccllaarree or llooccaall builtin commands (see the descriptions of ddeeccllaarree
+1 -1
View File
@@ -1449,7 +1449,7 @@ is applied to an array variable using compound assignment
below),
the variable's value is not unset
(as it is when using
.@ = ),
.Q = ),
and new values are appended to the array
beginning at one greater than the array's maximum index (for indexed arrays)
or added as additional key\-value pairs in an associative array.
+6
View File
@@ -9836,6 +9836,12 @@ the arguments as key sequences to bind.
Interactive shells will notify the user of completed jobs while sourcing a
script.
Newer versions defer notification until script execution completes.
@ignore
@item
Bash will not try to execute a shell function whose name contains a slash.
Previous versions disallowed this in @sc{posix} mode but allowed it by
default.
@end ignore
@end itemize
@end table
+4
View File
@@ -4669,7 +4669,11 @@ execute_simple_command (SIMPLE_COM *simple_command, int pipe_in, int pipe_out, i
builtin_is_special = 1;
}
if (builtin == 0)
#if 0 /*TAG bash-5.4 rob@landley.net 5/1/2025 */
func = ((shell_compatibility_level <= 52 && posixly_correct == 0) || absolute_program (words->word->word) == 0) ? find_function (words->word->word) : 0;
#else
func = (posixly_correct == 0 || absolute_program (words->word->word) == 0) ? find_function (words->word->word) : 0;
#endif
}
/* What happens in posix mode when an assignment preceding a command name
+70 -53
View File
@@ -57,8 +57,41 @@ void zreset (void);
int zungetc (int);
/* Provide one character of pushback whether we are using read or zread. */
static int zpushedchar = -1;
/* Provide 16 bytes of pushback whether we are using read or zread. Only used
by the read builtin when reading invalid multibyte characters. */
#define ZPUSHSIZE 16
static size_t zpushind, zpopind;
static unsigned char zpushbuf[ZPUSHSIZE];
static unsigned char zbufchar;
static inline int
zbufpop(unsigned char *cp)
{
if (zpushind == zpopind)
return (0);
*cp = zpushbuf[zpopind++];
if (zpopind == zpushind)
zpopind = zpushind = 0; /* reset, buffer empty */
return 1;
}
static inline int
zbufpush(int c)
{
if (zpushind == ZPUSHSIZE - 1)
return 0;
zpushbuf[zpushind++] = c;
return 1;
}
/* Add C to the pushback buffer. Can't push back EOF */
int
zungetc (int c)
{
zbufpush (c);
return c;
}
/* Read LEN bytes from FD into BUF. Retry the read on EINTR. Any other
error causes the loop to break. */
@@ -69,11 +102,10 @@ zread (int fd, char *buf, size_t len)
check_signals (); /* check for signals before a blocking read */
/* If we pushed a char back, return it immediately */
if (zpushedchar != -1)
/* If we pushed chars back, return the oldest one immediately */
if (zbufpop (&zbufchar))
{
*buf = (unsigned char)zpushedchar;
zpushedchar = -1;
*buf = zbufchar;
return 1;
}
@@ -114,11 +146,10 @@ zreadretry (int fd, char *buf, size_t len)
ssize_t r;
int nintr;
/* If we pushed a char back, return it immediately */
if (zpushedchar != -1)
/* If we pushed chars back, return the oldest one immediately */
if (zbufpop (&zbufchar))
{
*buf = (unsigned char)zpushedchar;
zpushedchar = -1;
*buf = zbufchar;
return 1;
}
@@ -143,14 +174,13 @@ zreadintr (int fd, char *buf, size_t len)
{
check_signals ();
/* If we pushed a char back, return it immediately */
if (zpushedchar != -1)
{
*buf = (unsigned char)zpushedchar;
zpushedchar = -1;
return 1;
}
/* If we pushed chars back, return the oldest one immediately */
if (zbufpop (&zbufchar))
{
*buf = zbufchar;
return 1;
}
return (read (fd, buf, len));
}
@@ -166,14 +196,13 @@ zreadc (int fd, char *cp)
{
ssize_t nr;
/* If we pushed a char back, return it immediately */
if (zpushedchar != -1 && cp)
{
*cp = (unsigned char)zpushedchar;
zpushedchar = -1;
return 1;
}
/* If we pushed chars back, return the oldest one immediately */
if (cp && zbufpop (&zbufchar))
{
*cp = zbufchar;
return 1;
}
if (lind == lused || lused == 0)
{
nr = zread (fd, lbuf, sizeof (lbuf));
@@ -197,12 +226,11 @@ zreadcintr (int fd, char *cp)
{
ssize_t nr;
/* If we pushed a char back, return it immediately */
if (zpushedchar != -1 && cp)
{
*cp = (unsigned char)zpushedchar;
zpushedchar = -1;
return 1;
/* If we pushed chars back, return the oldest one immediately */
if (cp && zbufpop (&zbufchar))
{
*cp = zbufchar;
return 1;
}
if (lind == lused || lused == 0)
@@ -228,11 +256,11 @@ zreadn (int fd, char *cp, size_t len)
{
ssize_t nr;
if (zpushedchar != -1 && cp)
{
*cp = zpushedchar;
zpushedchar = -1;
return 1;
/* If we pushed chars back, return the oldest one immediately */
if (cp && zbufpop (&zbufchar))
{
*cp = zbufchar;
return 1;
}
if (lind == lused || lused == 0)
@@ -253,25 +281,11 @@ zreadn (int fd, char *cp, size_t len)
return 1;
}
int
zungetc (int c)
{
if (zpushedchar == -1)
{
zpushedchar = c;
return c;
}
if (c == EOF || lind == 0)
return (EOF);
lbuf[--lind] = c; /* XXX */
return c;
}
void
zreset (void)
{
lind = lused = 0;
zpushind = zpopind = 0;
}
/* Sync the seek pointer for FD so that the kernel's idea of the last char
@@ -287,5 +301,8 @@ zsyncfd (int fd)
r = lseek (fd, -off, SEEK_CUR);
if (r != -1)
lused = lind = 0;
{
lused = lind = 0;
zpushind = zpopind = 0;
}
}