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;

Reply via email to