]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
timeout: handle all terminating signals
authorPádraig Brady <P@draigBrady.com>
Thu, 20 Nov 2025 16:02:43 +0000 (16:02 +0000)
committerPádraig Brady <P@draigBrady.com>
Fri, 21 Nov 2025 12:12:28 +0000 (12:12 +0000)
* src/timeout.c (term_sig): A new global list of all signals
whose default disposition is to terminate the process.
(install_cleanup): Iterate over the TERM_SIG list, rather than
installing the handler for a specific subset.
(block_cleanup_and_chld): Likewise.
* tests/timeout/timeout.sh: Add a test case for SIGPIPE.
* NEWS: Mention the bug fix.

NEWS
src/timeout.c
tests/timeout/timeout.sh

diff --git a/NEWS b/NEWS
index b021ed40a5afd97cd7614ab93e1684a29363f8ec..834c490d156dee504435917419261fc42f07e013 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   This also applies to the sha*sum and b2sum utilities.
   [This bug was present in "the beginning".]
 
+  'timeout' will now propagate all terminating signals to the monitored command.
+  Previously 'timeout' could have exited and left the monitored command running.
+  [bug introduced with timeout in coreutils-7.0]
+
   wc now documents its --debug option, currently used to
   indicate the line count acceleration being used.
   [bug introduced in coreutils-9.0]
index 3443cab4b9642302e5b154907bb4b71348a07736..541017cd7e9a883d2b31fb760143cf9c269fbd35 100644 (file)
 # define SA_RESTART 0
 #endif
 
+#ifndef SIGRTMIN
+# define SIGRTMIN 0
+# undef SIGRTMAX
+#endif
+#ifndef SIGRTMAX
+# define SIGRTMAX (SIGRTMIN - 1)
+#endif
+
 #define PROGRAM_NAME "timeout"
 
 #define AUTHORS proper_name_lite ("Padraig Brady", "P\303\241draig Brady")
@@ -191,6 +199,41 @@ chld (MAYBE_UNUSED int sig)
 {
 }
 
+static int const term_sig[] =
+  {
+    SIGALRM, /* our timeout.  */
+    SIGINT,  /* Ctrl-C at terminal for example.  */
+    SIGQUIT, /* Ctrl-\ at terminal for example.  */
+    SIGHUP,  /* terminal closed for example.  */
+    SIGTERM, /* if terminated, stop monitored proc.  */
+
+    SIGPIPE, SIGUSR1, SIGUSR2,
+    SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV,
+#ifdef SIGXCPU
+    SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+    SIGXFSZ,
+#endif
+#ifdef SIGSYS
+    SIGSYS,
+#endif
+#ifdef SIGVTALRM
+    SIGVTALRM,
+#endif
+#ifdef SIGPROF
+    SIGPROF,
+#endif
+#ifdef SIGPOLL
+    SIGPOLL,
+#endif
+#ifdef SIGPWR
+    SIGPWR,
+#endif
+#ifdef SIGSTKFLT
+    SIGSTKFLT,
+#endif
+  };
 
 static void
 cleanup (int sig)
@@ -410,11 +453,13 @@ install_cleanup (int sigterm)
   sa.sa_flags = SA_RESTART;   /* Restart syscalls if possible, as that's
                                  more likely to work cleanly.  */
 
-  sigaction (SIGALRM, &sa, nullptr); /* our timeout.  */
-  sigaction (SIGINT, &sa, nullptr);  /* Ctrl-C at terminal for example.  */
-  sigaction (SIGQUIT, &sa, nullptr); /* Ctrl-\ at terminal for example.  */
-  sigaction (SIGHUP, &sa, nullptr);  /* terminal closed for example.  */
-  sigaction (SIGTERM, &sa, nullptr); /* if killed, stop monitored proc.  */
+  for (int i = 0; i < countof (term_sig); i++)
+    sigaction (term_sig[i], &sa, nullptr);
+
+  /* Real Time signals also terminate by default.  */
+  for (int s = SIGRTMIN; s <= SIGRTMAX; s++)
+    sigaction (s, &sa, nullptr);
+
   sigaction (sigterm, &sa, nullptr); /* user specified termination signal.  */
 }
 
@@ -429,11 +474,12 @@ block_cleanup_and_chld (int sigterm, sigset_t *old_set)
   sigset_t block_set;
   sigemptyset (&block_set);
 
-  sigaddset (&block_set, SIGALRM);
-  sigaddset (&block_set, SIGINT);
-  sigaddset (&block_set, SIGQUIT);
-  sigaddset (&block_set, SIGHUP);
-  sigaddset (&block_set, SIGTERM);
+  for (int i = 0; i < countof (term_sig); i++)
+    sigaddset (&block_set, term_sig[i]);
+
+  for (int s = SIGRTMIN; s <= SIGRTMAX; s++)
+    sigaddset (&block_set, s);
+
   sigaddset (&block_set, sigterm);
 
   sigaddset (&block_set, SIGCHLD);
index e3defdd4e224bc11394e0d297533d03dc4a330d7..d799cbfd96c856a05d675cd1c179be5ec174e343 100755 (executable)
@@ -17,7 +17,7 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
-print_ver_ timeout
+print_ver_ timeout kill
 require_trap_signame_
 
 # no timeout
@@ -71,4 +71,12 @@ for opt in -v --verbose; do
   compare exp err || fail=1
 done
 
+# Ensure we propagate all terminating signals.
+# Specifically here we're testing that SIGPIPE is handled.
+# I.e., that we're not killed by the SIGPIPE (and leave the sleep running).
+# timeout would exit with 141 usually if SIGPIPE wasn't being handled.
+echo 125 > timeout.exp || framework_failure_
+{ timeout -v .1 sleep 10 2>&1; echo $? >timeout.status; } | :
+compare timeout.exp timeout.status || fail=1
+
 Exit $fail