]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
Bash-5.1 patch 12: fix race condition with child processes and resetting trapped...
authorChet Ramey <chet.ramey@case.edu>
Wed, 17 Nov 2021 21:47:24 +0000 (16:47 -0500)
committerChet Ramey <chet.ramey@case.edu>
Wed, 17 Nov 2021 21:47:24 +0000 (16:47 -0500)
command.h
execute_cmd.c
jobs.c
nojobs.c
patchlevel.h
sig.c
subst.c
trap.c

index 914198f9d4da00993de9f02415d45fb9b0622b1f..b84775280d032c7a435f1e2ea50d7236674e4f71 100644 (file)
--- a/command.h
+++ b/command.h
@@ -124,6 +124,7 @@ enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select,
 #define SUBSHELL_PROCSUB 0x20  /* subshell caused by <(command) or >(command) */
 #define SUBSHELL_COPROC        0x40    /* subshell from a coproc pipeline */
 #define SUBSHELL_RESETTRAP 0x80        /* subshell needs to reset trap strings on first call to trap */
+#define SUBSHELL_IGNTRAP 0x100  /* subshell should reset trapped signals from trap_handler */
 
 /* A structure which represents a word. */
 typedef struct word_desc {
index 90129e066313dae1b818262d30ef4e3f2d7f7dd1..425679a20f3f1b5e448bfe1dbb2f0055a89e3800 100644 (file)
@@ -1547,6 +1547,9 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
   clear_pending_traps ();
   reset_signal_handlers ();
   subshell_environment |= SUBSHELL_RESETTRAP;
+  /* Note that signal handlers have been reset, so we should no longer
+    reset the handler and resend trapped signals to ourselves. */
+  subshell_environment &= ~SUBSHELL_IGNTRAP;
 
   /* We are in a subshell, so forget that we are running a trap handler or
      that the signal handler has changed (we haven't changed it!) */
@@ -4320,7 +4323,8 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
          already_forked = 1;
          cmdflags |= CMD_NO_FORK;
 
-         subshell_environment = SUBSHELL_FORK;         /* XXX */
+         /* We redo some of what make_child() does with SUBSHELL_IGNTRAP */
+         subshell_environment = SUBSHELL_FORK|SUBSHELL_IGNTRAP;        /* XXX */
          if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
            subshell_environment |= SUBSHELL_PIPE;
          if (async)
@@ -4574,6 +4578,7 @@ run_builtin:
             trap strings if we run trap to change a signal disposition. */
          reset_signal_handlers ();
          subshell_environment |= SUBSHELL_RESETTRAP;
+         subshell_environment &= ~SUBSHELL_IGNTRAP;
 
          if (async)
            {
@@ -5514,6 +5519,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
       reset_terminating_signals ();    /* XXX */
       /* Cancel traps, in trap.c. */
       restore_original_signals ();
+      subshell_environment &= ~SUBSHELL_IGNTRAP;
 
 #if defined (JOB_CONTROL)
       FREE (p);
diff --git a/jobs.c b/jobs.c
index a581f3053fdab36820288d3f64b8eb22c81db863..7c3b6e838f64c00460671cc9b82bcaca33c73ffd 100644 (file)
--- a/jobs.c
+++ b/jobs.c
@@ -2217,6 +2217,8 @@ make_child (command, flags)
         signals to the default state for a new process. */
       pid_t mypid;
 
+      subshell_environment |= SUBSHELL_IGNTRAP;
+
       /* If this ends up being changed to modify or use `command' in the
         child process, go back and change callers who free `command' in
         the child process when this returns. */
index c5fc83d90a1538fd7d50fd898662219ec272c003..f2563ca09b8a4e1e88cf819747378d663fe86fcf 100644 (file)
--- a/nojobs.c
+++ b/nojobs.c
@@ -575,6 +575,8 @@ make_child (command, flags)
        last_asynchronous_pid = getpid ();
 #endif
 
+      subshell_environment |= SUBSHELL_IGNTRAP;
+
       default_tty_job_signals ();
     }
   else
index 8b14f289a2dc4a9342a5ce978148ec7ea6196fda..eb2aca528af3a0d2c49f392527ca05eeb310f529 100644 (file)
@@ -25,6 +25,6 @@
    regexp `^#define[   ]*PATCHLEVEL', since that's what support/mkversion.sh
    looks for to find the patch level (for the sccs version string). */
 
-#define PATCHLEVEL 11
+#define PATCHLEVEL 12
 
 #endif /* _PATCHLEVEL_H_ */
diff --git a/sig.c b/sig.c
index 6964d862cb76e15e207720e20dcfefcb6d20600c..e6537d2673b5db7b3d6c43c7afbe11dd9a262b58 100644 (file)
--- a/sig.c
+++ b/sig.c
@@ -55,7 +55,8 @@
 #  include "bashhist.h"
 #endif
 
-extern void initialize_siglist ();
+extern void initialize_siglist PARAMS((void));
+extern void set_original_signal PARAMS((int, SigHandler *));
 
 #if !defined (JOB_CONTROL)
 extern void initialize_job_signals PARAMS((void));
@@ -255,6 +256,13 @@ initialize_terminating_signals ()
       sigaction (XSIG (i), &act, &oact);
       XHANDLER(i) = oact.sa_handler;
       XSAFLAGS(i) = oact.sa_flags;
+
+#if 0
+      set_original_signal (XSIG(i), XHANDLER(i));      /* optimization */
+#else
+      set_original_signal (XSIG(i), act.sa_handler);   /* optimization */
+#endif
+
       /* Don't do anything with signals that are ignored at shell entry
         if the shell is not interactive. */
       /* XXX - should we do this for interactive shells, too? */
diff --git a/subst.c b/subst.c
index 462752de61eb7b78b50c656e1dfd66af7cab7e1d..327de08369ace2113654e470c7588a372d713ac5 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -5951,6 +5951,7 @@ process_substitute (string, open_for_read_in_child)
       free_pushed_string_input ();
       /* Cancel traps, in trap.c. */
       restore_original_signals ();     /* XXX - what about special builtins? bash-4.2 */
+      subshell_environment &= ~SUBSHELL_IGNTRAP;
       QUIT;    /* catch any interrupts we got post-fork */
       setup_async_signals ();
 #if 0
@@ -6382,6 +6383,7 @@ command_substitute (string, quoted, flags)
        }       
       QUIT;    /* catch any interrupts we got post-fork */
       subshell_environment |= SUBSHELL_RESETTRAP;
+      subshell_environment &= ~SUBSHELL_IGNTRAP;
     }
 
 #if defined (JOB_CONTROL)
diff --git a/trap.c b/trap.c
index c7f8ded56f7dbe20d4178853bca0f478bad6efbb..1b27fb3a3a8c1918f93e30e05a3a924e31ff92cd 100644 (file)
--- a/trap.c
+++ b/trap.c
@@ -481,6 +481,32 @@ trap_handler (sig)
       SIGRETURN (0);
     }
 
+  /* This means we're in a subshell, but have not yet reset the handler for
+     trapped signals. We're not supposed to execute the trap in this situation;
+     we should restore the original signal and resend the signal to ourselves
+     to preserve the Posix "signal traps that are not being ignored shall be
+     set to the default action" semantics. */
+  if ((subshell_environment & SUBSHELL_IGNTRAP) && trap_list[sig] != (char *)IGNORE_SIG)
+    {
+      sigset_t mask;
+
+      /* Paranoia */
+      if (original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER)
+       original_signals[sig] = SIG_DFL;
+
+      restore_signal (sig);
+
+      /* Make sure we let the signal we just caught through */
+      sigemptyset (&mask);
+      sigprocmask (SIG_SETMASK, (sigset_t *)NULL, &mask);
+      sigdelset (&mask, sig);
+      sigprocmask (SIG_SETMASK, &mask, (sigset_t *)NULL);
+
+      kill (getpid (), sig);
+
+      SIGRETURN (0);
+    }
+
   if ((sig >= NSIG) ||
       (trap_list[sig] == (char *)DEFAULT_SIG) ||
       (trap_list[sig] == (char *)IGNORE_SIG))