From: Chet Ramey Date: Sun, 4 May 2025 21:21:25 +0000 (-0400) Subject: fix for command substitution string parsing that resulted in top-level unwind-protect... X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=535a8150b65ee6888f54f602274dbbdcd77c788e;p=thirdparty%2Fbash.git 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 --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index a96c3dc4..58c4975a 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -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 Александр Ушаков + + 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 diff --git a/builtins/evalstring.c b/builtins/evalstring.c index 8a9cf2d7..d962f825 100644 --- a/builtins/evalstring.c +++ b/builtins/evalstring.c @@ -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); } diff --git a/builtins/read.def b/builtins/read.def index 98bb0470..3b13385f 100644 --- a/builtins/read.def +++ b/builtins/read.def @@ -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 */ } diff --git a/config.h.in b/config.h.in index 7dac61a9..1367eaaa 100644 --- a/config.h.in +++ b/config.h.in @@ -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 diff --git a/configure b/configure index 0ef72d76..b7da824d 100755 --- a/configure +++ b/configure @@ -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 diff --git a/configure.ac b/configure.ac index a9e8ce93..fdf542fa 100644 --- a/configure.ac +++ b/configure.ac @@ -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 . -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 diff --git a/doc/bash.0 b/doc/bash.0 index 9d95e20b..e01a1afe 100644 --- a/doc/bash.0 +++ b/doc/bash.0 @@ -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 diff --git a/doc/bash.1 b/doc/bash.1 index b03c9906..5af3492f 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -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. diff --git a/doc/bashref.texi b/doc/bashref.texi index dd3f4a30..06c45564 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -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 diff --git a/execute_cmd.c b/execute_cmd.c index 7387e730..7f0b61cf 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -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 diff --git a/lib/sh/zread.c b/lib/sh/zread.c index a3e12abd..9246d1e2 100644 --- a/lib/sh/zread.c +++ b/lib/sh/zread.c @@ -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; + } }