Configuration Information:
Machine: x86_64
OS: linux-gnu
Bash Version: 5.3.9(1)-release, also devel a4a9dbc
Description:
With set -eE and an ERR trap, a second SIGTERM that arrives while the
TERM trap function is still running makes bash spin at 100% CPU, with
no system calls, after printing
"warning: unwind_frame_discard: pe_dispose: frame not found".
The first TERM interrupts `wait', which fails, so the ERR trap starts
and runs the pending TERM trap from parse_command(). The second TERM
runs the trap again inside the first one, and `false' there triggers
errexit. The innermost parse_and_execute calls reset_local_contexts(),
so pop_context() of the outer trap function leaves variable_context
at -1. The ERR trap's parse_and_execute then gets ERREXIT before it
has begun a "pe_dispose" frame (command == NULL), but
discard_unwind_frame ("pe_dispose") still runs. It throws away the
whole unwind-protect list, including the PE_TAG frame that restores
top_level, so jump_to_top_level (ERREXIT) keeps landing on the same
setjmp.
Repeat-By:
Run this as a script file (bash -c doesn't trigger it):
set -eE
trap 'echo err >&2' ERR
stop() { sleep 1; false; }
trap stop TERM
(sleep 1; kill -TERM $$; sleep 0.3; kill -TERM $$) &
sleep 30 & wait "$!"
Fix:
The other cases in the switch only touch "pe_dispose" when command is
set, so ERREXIT should check that too. With this the script exits
with status 1, and run-set-e, run-func and run-errors still pass.
--- a/builtins/evalstring.c
+++ b/builtins/evalstring.c
@@ -387,7 +387,8 @@ parse_and_execute (char *string, const char *from_file,
int flags)
unwound. */
if (exit_immediately_on_error && variable_context)
{
- discard_unwind_frame ("pe_dispose");
+ if (command)
+ discard_unwind_frame ("pe_dispose");
reset_local_contexts (); /* not in a function */
}
should_jump_to_top_level = 1;