Configuration Information [Automatically generated, do not change]: Machine: i586 OS: linux-gnu Compiler: gcc Compilation CFLAGS: -DPROGRAM='bash' -DCONF_HOSTTYPE='i586' -DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='i586-pc-linux-gnu' -DCONF_ uname output: Linux debian 4.1.0 #1 SMP Sat Jul 11 10:34:14 CEST 2015 i686 GNU/Linux Machine Type: i586-pc-linux-gnu Bash Version: 4.3 Patch Level: 30 Release Status: release Description:
Setting the errtrace shell option makes subshells inherit the ERR trap of the parent. This is working fine but the ERR trap is silently reset in the subshell when another, unrelated signal trap is installed. Repeat-By: Execute the following script. The execution should stop in the shell function at the 'false' command due to the ERR trap and the errtrace option. $ cat > test.sh <<'EOF' #!/bin/bash set -o errtrace trap 'echo "ERR trap called at level $BASH_SUBSHELL" ; exit 1' ERR ( echo "Subshell inherited traps:" trap # install unrelated EXIT trap trap 'echo "Subshell EXIT trap called: $?"' EXIT echo "Traps after installing additional EXIT trap:" trap # should fail but doesn't false echo "SHOULD NOT BE REACHED" ) echo "SHOULD NOT BE REACHED EITHER" EOF $ bash ./test.sh Subshell inherited traps: trap -- 'echo "ERR trap called at level $BASH_SUBSHELL" ; exit 1' ERR Traps after installing additional EXIT trap: trap -- 'echo "Subshell EXIT trap called: $?"' EXIT SHOULD NOT BE REACHED Subshell EXIT trap called: 0 SHOULD NOT BE REACHED EITHER This bug is still present in the latest version: $ ./bash --version GNU bash, version 4.4.0(4)-beta (i686-pc-linux-gnu) Fix: The culprit seems to be free_trap_strings(). This function is invoked in a subshell to cleanup allocated strings. *But* it purges the shell special traps (DEBUG, ERR, RETURN) too, even though they were left in place by reset_or_restore_signal_handlers() if the right options were set. The following patch fixes the problem but I'm not sure if this is correct. At least the test case is working then as expected: $ ./bash ./test.sh Subshell inherited traps: trap -- 'echo "ERR trap called at level $BASH_SUBSHELL" ; exit 1' ERR Traps after installing additional EXIT trap: trap -- 'echo "Subshell EXIT trap called: $?"' EXIT trap -- 'echo "ERR trap called at level $BASH_SUBSHELL" ; exit 1' ERR ERR trap called at level 1 Subshell EXIT trap called: 1 ERR trap called at level 0 Regards, Jan --- trap.c.orig 2015-09-26 16:39:31.000000000 +0200 +++ trap.c 2016-02-18 21:30:01.116852477 +0100 @@ -1140,12 +1140,31 @@ { register int i; - for (i = 0; i < BASH_NSIG; i++) + for (i = 0; i < NSIG; i++) { if (trap_list[i] != (char *)IGNORE_SIG) free_trap_string (i); } - trap_list[DEBUG_TRAP] = trap_list[EXIT_TRAP] = trap_list[ERROR_TRAP] = trap_list[RETURN_TRAP] = (char *)NULL; + + if (trap_list[EXIT_TRAP]) + free_trap_string (EXIT_TRAP); + trap_list[EXIT_TRAP] = (char *)NULL; + + if (function_trace_mode == 0) + { + if (trap_list[DEBUG_TRAP]) + free_trap_string (DEBUG_TRAP); + if (trap_list[RETURN_TRAP]) + free_trap_string (RETURN_TRAP); + trap_list[DEBUG_TRAP] = trap_list[RETURN_TRAP] = (char *)NULL; + } + + if (error_trace_mode == 0) + { + if (trap_list[ERROR_TRAP]) + free_trap_string (ERROR_TRAP); + trap_list[ERROR_TRAP] = (char *)NULL; + } } /* Free a trap command string associated with SIG without changing signal