fix for potential read builtin crash if bash gets a signal before read reads any input; make wait -f work if there are no pid or job arguments and update documentation; save and restore variable reflected in $BASH_COMMAND around shell function calls

This commit is contained in:
Chet Ramey
2025-09-18 17:54:55 -04:00
parent 013fae2c98
commit cf8a2518c8
15 changed files with 136 additions and 28 deletions
+27
View File
@@ -11768,3 +11768,30 @@ lib/sh/strtrans.c
pointer when encountering an invalid wide character by using our
own mbstate_t and reinitializing it on EILSEQ.
Report and patch from Grisha Levit <grishalevit@gmail.com>
9/17
----
builtins/read.def
- read_builtin: make sure i is >= 0 after a timeout longjmp before
trying to terminate input_string
From a report from Duncan Roe <duncan_roe@optusnet.com.au>
jobs.c,jobs.h
- wait_for_background_pids: now takes a new first argument, WFLAGS.
Used by wait builtin to pass through JWAIT_FORCE
builtins/wait.def
- wait_builtin: if wait doesn't have any pid or job arguments, make
sure to pass JWAIT_FORCE through to wait_for_background_pids so
we make sure to wait for all background jobs to terminate
Inspired by report from Robert Elz <kre@munnari.oz.au>
doc/bash.1,doc/bashref.texi
- wait: update description of -f to include wait with no arguments
9/18
----
execute_cmd.c
- execute_function: save and restore the_printed_command_except_trap
(reflected in $BASH_COMMAND) around shell function calls
From a report by Mike Jonkmans <bashbug@jonkmans.nl>
+1
View File
@@ -1616,6 +1616,7 @@ tests/trap6.sub f
tests/trap7.sub f
tests/trap8.sub f
tests/trap9.sub f
tests/trap10.sub f
tests/type.tests f
tests/type.right f
tests/type1.sub f
+2 -1
View File
@@ -538,7 +538,8 @@ read_builtin (WORD_LIST *list)
so we have to save input_string temporarily, run the unwind-
protects, then restore input_string so we can use it later */
orig_input_string = 0;
input_string[i] = '\0'; /* make sure it's terminated */
if (i >= 0)
input_string[i] = '\0'; /* make sure it's terminated */
if (i == 0)
{
t = (char *)xmalloc (1);
+1 -1
View File
@@ -251,7 +251,7 @@ wait_builtin (WORD_LIST *list)
currently active background processes. */
if (list == 0)
{
opt = wait_for_background_pids (&pstat);
opt = wait_for_background_pids (wflags, &pstat);
#if 0
/* Compatibility with NetBSD sh: don't set VNAME since it doesn't
correspond to the return status. */
+1
View File
@@ -192,6 +192,7 @@ typedef struct element {
#define CMD_LASTPIPE 0x2000
#define CMD_STDPATH 0x4000 /* use standard path for command lookup */
#define CMD_TRY_OPTIMIZING 0x8000 /* try to optimize this simple command */
#define CMD_WANT_ERR_TRAP 0x10000
/* What a command looks like. */
typedef struct command {
+2
View File
@@ -13038,6 +13038,8 @@ This is useful only when used with the \fB\-n\fP option.
Supplying the \fB\-f\fP option, when job control is enabled,
forces \fBwait\fP to wait for each \fIid\fP to terminate before
returning its status, instead of returning when it changes status.
If there are no \fIid\fP arguments,
\fBwait\fP waits until all background processes have terminated.
.IP
If none of the \fIid\fPs specify one of the shell's active child
processes, the return status is 127.
+2
View File
@@ -10198,6 +10198,8 @@ This is useful only when used with the @option{-n} option.
Supplying the @option{-f} option, when job control is enabled,
forces @code{wait} to wait for each @var{id} to terminate before
returning its status, instead of returning when it changes status.
If there are no @var{id} arguments,
@code{wait} waits until all background processes have terminated.
If none of the @var{id}s specify one of the shell's an active child
processes, the return status is 127.
+43 -10
View File
@@ -335,6 +335,8 @@ do { \
} \
do { } while (0)
#define ERROR_TRAP_SET() (signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0)
/* A sort of function nesting level counter */
int funcnest = 0;
int funcnest_max = 0;
@@ -639,7 +641,8 @@ async_redirect_stdin (void)
int
execute_command_internal (COMMAND *command, int asynchronous, int pipe_in, int pipe_out, struct fd_bitmap *fds_to_close)
{
int exec_result, user_subshell, invert, ignore_return, was_error_trap, fork_flags;
int exec_result, user_subshell, invert, ignore_return, fork_flags;
int was_error_trap, want_to_run_error_trap;
REDIRECT *my_undo_list, *exec_undo_list;
char *tcmd;
volatile int save_line_number;
@@ -769,7 +772,7 @@ execute_command_internal (COMMAND *command, int asynchronous, int pipe_in, int p
if (asynchronous == 0)
{
was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
was_error_trap = ERROR_TRAP_SET ();
invert = (command->flags & CMD_INVERT_RETURN) != 0;
ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
@@ -867,7 +870,7 @@ execute_command_internal (COMMAND *command, int asynchronous, int pipe_in, int p
/* Handle WHILE FOR CASE etc. with redirections. (Also '&' input
redirection.) */
was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
was_error_trap = ERROR_TRAP_SET ();
ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
if (do_redirections (command->redirects, RX_ACTIVE|RX_UNDOABLE) != 0)
@@ -930,13 +933,19 @@ execute_command_internal (COMMAND *command, int asynchronous, int pipe_in, int p
#if defined (RECYCLES_PIDS)
last_made_pid = NO_PID;
#endif
was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
was_error_trap = ERROR_TRAP_SET ();
want_to_run_error_trap = ignore_return == 0 && invert == 0 &&
pipe_in == NO_PIPE && pipe_out == NO_PIPE &&
(command->value.Simple->flags & CMD_COMMAND_BUILTIN) == 0;
if ((ignore_return || invert) && command->value.Simple)
command->value.Simple->flags |= CMD_IGNORE_RETURN;
if (command->flags & CMD_STDIN_REDIR)
command->value.Simple->flags |= CMD_STDIN_REDIR;
if (want_to_run_error_trap)
command->value.Simple->flags |= CMD_WANT_ERR_TRAP;
begin_unwind_frame ("simple_lineno");
add_unwind_protect (uw_restore_lineno, (void *) (intptr_t) save_line_number);
@@ -1009,10 +1018,10 @@ execute_command_internal (COMMAND *command, int asynchronous, int pipe_in, int p
trap if the command run by the `command' builtin fails; we want to
defer that until the command builtin itself returns failure. */
/* 2020/07/14 -- this changes with how the command builtin is handled */
if (was_error_trap && ignore_return == 0 && invert == 0 &&
pipe_in == NO_PIPE && pipe_out == NO_PIPE &&
(command->value.Simple->flags & CMD_COMMAND_BUILTIN) == 0 &&
exec_result != EXECUTION_SUCCESS)
/* XXX - what happens if a function is called that sets the ERR trap
then returns a non-zero exit status? Have to check here using
ERROR_TRAP_SET() instead of relying on was_error_trap */
if (was_error_trap && want_to_run_error_trap && exec_result != EXECUTION_SUCCESS)
{
last_command_exit_value = exec_result;
line_number = line_number_for_err_trap;
@@ -1147,7 +1156,7 @@ execute_command_internal (COMMAND *command, int asynchronous, int pipe_in, int p
case cm_cond:
#endif
case cm_function_def:
was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
was_error_trap = ERROR_TRAP_SET ();
#if defined (DPAREN_ARITHMETIC)
if (ignore_return && command->type == cm_arith)
command->value.Arith->flags |= CMD_IGNORE_RETURN;
@@ -2920,7 +2929,7 @@ execute_connection (COMMAND *command, int asynchronous, int pipe_in, int pipe_ou
break;
case '|':
was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
was_error_trap = ERROR_TRAP_SET ();
SET_LINE_NUMBER (line_number); /* XXX - save value? */
exec_result = execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close);
@@ -5144,6 +5153,16 @@ uw_restore_funcarray_state (void *fa)
}
#endif
static void
restore_bash_command (void *oldcmd)
{
if (the_printed_command_except_trap != (char *)oldcmd)
{
FREE (the_printed_command_except_trap);
the_printed_command_except_trap = oldcmd;
}
}
static void
function_misc_cleanup (void)
{
@@ -5168,6 +5187,9 @@ execute_function (SHELL_VAR *var, WORD_LIST *words, int flags, struct fd_bitmap
int return_val, result, lineno;
COMMAND *tc, *fc, *save_current;
char *debug_trap, *error_trap, *return_trap;
#if 0
int have_error_trap, want_to_run_error_trap;
#endif
#if defined (ARRAY_VARS)
SHELL_VAR *funcname_v, *bash_source_v, *bash_lineno_v;
ARRAY *funcname_a;
@@ -5200,6 +5222,11 @@ execute_function (SHELL_VAR *var, WORD_LIST *words, int flags, struct fd_bitmap
if (tc && (flags & CMD_IGNORE_RETURN))
tc->flags |= CMD_IGNORE_RETURN;
#if 0
have_error_trap = ERROR_TRAP_SET () && error_trace_mode;
want_to_run_error_trap = flags & CMD_WANT_ERR_TRAP;
#endif
/* A limited attempt at optimization: shell functions at the end of command
substitutions that are already marked NO_FORK. */
if (tc && (flags & CMD_NO_FORK) && (subshell_environment & SUBSHELL_COMSUB))
@@ -5232,6 +5259,7 @@ execute_function (SHELL_VAR *var, WORD_LIST *words, int flags, struct fd_bitmap
unwind_protect_pointer (this_shell_function);
unwind_protect_int (funcnest);
unwind_protect_int (loop_level);
add_unwind_protect (restore_bash_command, savestring (the_printed_command_except_trap));
}
else
push_context (var->name, subshell, temporary_env); /* don't unwind-protect for subshells */
@@ -5361,6 +5389,11 @@ execute_function (SHELL_VAR *var, WORD_LIST *words, int flags, struct fd_bitmap
save_current = currently_executing_command;
if (from_return_trap == 0)
run_return_trap ();
#if 0
/* An ERR trap on a return <non-zero> won't be run anywhere else */
if (result != EXECUTION_SUCCESS && have_error_trap && want_to_run_error_trap)
run_error_trap ();
#endif
currently_executing_command = save_current;
}
else
+2 -4
View File
@@ -2096,8 +2096,6 @@ print_pipeline (PROCESS *p, int job_index, int format, FILE *stream)
if (es == 0)
es = 2; /* strlen ("| ") */
name_padding = LONGEST_SIGNAL_DESC - es;
if (name_padding <= 0)
name_padding = 1;
fprintf (stream, "%*s", name_padding, "");
@@ -2795,7 +2793,7 @@ no_child:
/* Wait for all of the background processes started by this shell to finish. */
int
wait_for_background_pids (struct procstat *ps)
wait_for_background_pids (int wflags, struct procstat *ps)
{
register int i, r;
int any_stopped, check_async, njobs;
@@ -2835,7 +2833,7 @@ wait_for_background_pids (struct procstat *ps)
UNBLOCK_CHILD (oset);
QUIT;
errno = 0; /* XXX */
r = wait_for_single_pid (pid, JWAIT_PERROR);
r = wait_for_single_pid (pid, JWAIT_PERROR|wflags);
if (ps)
{
ps->pid = pid;
+1 -1
View File
@@ -305,7 +305,7 @@ extern int job_exit_signal (int);
extern int process_exit_status (WAIT);
extern int wait_for_single_pid (pid_t, int);
extern int wait_for_background_pids (struct procstat *);
extern int wait_for_background_pids (int, struct procstat *);
extern int wait_for (pid_t, int);
extern int wait_for_job (int, int, struct procstat *);
extern int wait_for_any_job (int, struct procstat *);
+1 -1
View File
@@ -180,7 +180,7 @@ bash: -c: line 1: unexpected argument `<' to conditional unary operator
bash: -c: line 1: syntax error near `<'
bash: -c: line 1: `[[ -n < ]]'
ERR: 22: -[[ -n $unset ]]- failed
ERR: 28: -[[ -z nonempty ]]- failed
ERR: 28: -func- failed
+ [[ -t X ]]
./cond-xtrace1.sub: line 6: [[: X: integer expected
+ [[ '' > 7 ]]
+8 -3
View File
@@ -120,15 +120,20 @@ exit=0
A
In trap-argument: last command preceding the trap action
In a function call: last command in the trap action
ERR: 23: -func- failed
ERR: 20: -[[ -z nonempty ]]- failed
ERR: 27: -func- failed
ERR: 35: -func2- failed
ERR: 39: -func2- failed
caught a child death
caught a child death
caught a child death
trap -- 'echo caught a child death' SIGCHLD
echo caught a child death
./trap.tests: line 118: trap: cannot specify both -p and -P
./trap.tests: line 119: trap: -P requires at least one signal name
./trap.tests: line 121: trap: cannot specify both -p and -P
./trap.tests: line 122: trap: -P requires at least one signal name
trap: usage: trap [-Plp] [[action] signal_spec ...]
./trap.tests: line 121: trap: -x: invalid option
./trap.tests: line 124: trap: -x: invalid option
trap: usage: trap [-Plp] [[action] signal_spec ...]
trap -- 'echo exiting' EXIT
trap -- 'echo aborting' SIGABRT
+3
View File
@@ -99,6 +99,9 @@ ${THIS_SH} ./trap8.sub
# return without argument in trap string
${THIS_SH} ./trap9.sub
# ERR trap and functions
${THIS_SH} ./trap10.sub
#
# show that setting a trap on SIGCHLD is not disastrous.
#
+40
View File
@@ -0,0 +1,40 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
trap 'echo ERR: $LINENO: -$BASH_COMMAND- failed' ERR
# the conditional command will fail, then func will fail
func()
{
[[ -z nonempty ]]
}
# func will fail
func
set -E
# the conditional command will fail, then func will fail
func
set +E
func2()
{
return 1
}
# func2 will fail
func2
# if we change so that `return 1' triffers the ERR trap, this will change
set -E
func2
set +E
+2 -7
View File
@@ -50,13 +50,8 @@ exit 42 | command false
command command command false
unset FALSE
if [ -x /bin/false ]; then
FALSE=/bin/false
elif [ -x /usr/bin/false ]; then
FALSE=/usr/bin/false
elif [ -x /usr/local/bin/false ]; then
FALSE=/usr/local/bin/false
else
FALSE=$(type -P false)
if [ -z "$FALSE" ]; then
FALSE='command false'
fi