mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-27 07:43:07 +02:00
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:
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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. */
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user