There was a narrow race condition reported at:
https://github.com/coreutils/coreutils/issues/82
Then I noticed another related race condition
in the case of failing fork().
Both should be addressed by the attached.
cheers,
Pádraig
From c1cf5148a1c6302d27661ff0af772de1e7dbb2b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <p...@draigbrady.com>
Date: Mon, 11 Mar 2024 13:18:37 +0000
Subject: [PATCH 1/2] timeout: fix race where we might kill arbitrary processes
* src/timeout.c (cleanup): Handle the case where monitored_pid
might be -1, which could happen if a signal was received
immediately after a failed fork() call. In that case it would
send the termination signal to all processes that the timeout
process has permission to send signals too.
* NEWS: Mention the bug fix.
---
NEWS | 4 ++++
src/timeout.c | 11 ++++++++---
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/NEWS b/NEWS
index 70c4e2f44..864d7fe8b 100644
--- a/NEWS
+++ b/NEWS
@@ -33,6 +33,10 @@ GNU coreutils NEWS -*- outline -*-
on systems with a page size larger than the stdio BUFSIZ.
[This bug was present in "the beginning".]
+ timeout avoids a narrow race condition, where it might kill arbitrary
+ processes after a failed process fork.
+ [bug introduced with timeout in coreutils-7.0]
+
wc no longer fails to count unprintable characters as parts of words.
[bug introduced in textutils-2.1]
diff --git a/src/timeout.c b/src/timeout.c
index 85d97c0b5..9aa46a4f5 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -207,7 +207,7 @@ cleanup (int sig)
timed_out = 1;
sig = term_signal;
}
- if (monitored_pid)
+ if (0 < monitored_pid)
{
if (kill_after)
{
@@ -244,8 +244,13 @@ cleanup (int sig)
}
}
}
- else /* we're the child or the child is not exec'd yet. */
- _exit (128 + sig);
+ else if (monitored_pid == -1)
+ { /* were in the parent, so let it continue to exit below. */
+ }
+ else /* monitored_pid == 0 */
+ { /* we're the child or the child is not exec'd yet. */
+ _exit (128 + sig);
+ }
}
void
--
2.43.0
From 347cd7cf68501bb00db965af48cf1bc47446e648 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <p...@draigbrady.com>
Date: Mon, 11 Mar 2024 13:46:24 +0000
Subject: [PATCH 2/2] timeout: fix narrow race in failing to kill processes
* src/timeout.c (cleanup): Detect when were in the possible
problematic window where monitored_pid is not initialized in the parent
while the child may be running. Instead record the signal for
handling later in the parent in ...
(main): Resend the cleanup signal now that we know monitored_pid is set.
* NEWS: Mention the bug fix.
Reported at https://github.com/coreutils/coreutils/issues/82
---
NEWS | 4 ++++
src/timeout.c | 12 ++++++++++--
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/NEWS b/NEWS
index 864d7fe8b..20cadf183 100644
--- a/NEWS
+++ b/NEWS
@@ -37,6 +37,10 @@ GNU coreutils NEWS -*- outline -*-
processes after a failed process fork.
[bug introduced with timeout in coreutils-7.0]
+ timeout avoids a narrow race condition, where it might fail to
+ kill monitored processes immediately after forking them.
+ [bug introduced with timeout in coreutils-7.0]
+
wc no longer fails to count unprintable characters as parts of words.
[bug introduced in textutils-2.1]
diff --git a/src/timeout.c b/src/timeout.c
index 9aa46a4f5..a4ea86712 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -78,7 +78,8 @@
static int timed_out;
static int term_signal = SIGTERM; /* same default as kill command. */
-static pid_t monitored_pid;
+static pid_t monitored_pid = -2;
+static int pending_cleanup_signal;
static double kill_after;
static bool foreground; /* whether to use another program group. */
static bool preserve_status; /* whether to use a timeout status or not. */
@@ -247,8 +248,12 @@ cleanup (int sig)
else if (monitored_pid == -1)
{ /* were in the parent, so let it continue to exit below. */
}
+ else if (monitored_pid == -2)
+ { /* parent or child, where fork return value not assigned yet. */
+ pending_cleanup_signal = sig; /* flag for parent to handle later. */
+ }
else /* monitored_pid == 0 */
- { /* we're the child or the child is not exec'd yet. */
+ { /* we're the child and have not exec'd yet. */
_exit (128 + sig);
}
}
@@ -561,6 +566,9 @@ main (int argc, char **argv)
pid_t wait_result;
int status;
+ if (pending_cleanup_signal)
+ raise (pending_cleanup_signal); /* Handle with monitored_pid set. */
+
/* We configure timers so that SIGALRM is sent on expiry.
Therefore ensure we don't inherit a mask blocking SIGALRM. */
unblock_signal (SIGALRM);
--
2.43.0