mirror of
https://github.com/coreutils/coreutils.git
synced 2026-10-28 09:44:34 +08:00
timeout: ensure we timeout with --verbose failures
In the edge case where --verbose diagnostics might block or induce signals, ensure we always timeout the monitored command and do not loop diagnosing diagnostic induced signals. * NEWS: Mention the bug fix. * src/timeout.c (cleanup): Avoid diagnosing SIGPIPE and SIGXFSZ. Also send the signal before diagnosing to ensure command is timed out. * tests/env/env-signal-handler.sh: Run timeout(1) with -f to ensure the SIGKILL is diagnosed. * tests/timeout/timeout.sh: Likewise. Also add test cases for SIGPIPE and SIGXFSZ. Discovered by Anthropic's OSS Scanner, as bug ANT-2026-GV4Z4T3T.
This commit is contained in:
1 parent
074eb270fe
commit
8fcaebfe47
4 files changed
+58
-12
No files matched your search
@@ -81,6 +81,12 @@ GNU coreutils NEWS -*- outline -*-
|
||||
if events arrived faster than tail could process them.
|
||||
[bug introduced in coreutils-7.5]
|
||||
|
||||
'timeout --verbose' no longer hangs or loops forever in the edge case
|
||||
where writing the diagnostics would block or generate a signal respectively.
|
||||
Instead these edge cases will not have a corresponding diagnostic issued.
|
||||
Also --kill-after without --foreground, will not have a diagnostic issued.
|
||||
[hang bug introduced in coreutils-8.29, and loop bug in coreutils 9.10]
|
||||
|
||||
'unexpand' no longer overflows a heap buffer on non-GLIBC platforms,
|
||||
given whitespace characters whose display width is negative. E.g.,
|
||||
this edge case can occur with \u0085 (next line) characters on musl.
|
||||
|
||||
+19
-8
@@ -203,6 +203,12 @@ chld (MAYBE_UNUSED int sig)
|
||||
static void
|
||||
cleanup (int sig)
|
||||
{
|
||||
/* Don't diagnose signals that the diagnostic itself could generate. */
|
||||
bool diagnose = verbose && sig != SIGPIPE;
|
||||
#ifdef SIGXFSZ
|
||||
diagnose &= sig != SIGXFSZ;
|
||||
#endif
|
||||
|
||||
if (sig == SIGALRM)
|
||||
{
|
||||
timed_out = 1;
|
||||
@@ -229,14 +235,6 @@ cleanup (int sig)
|
||||
/* Send the signal directly to the monitored child,
|
||||
in case it has itself become group leader,
|
||||
or is not running in a separate group. */
|
||||
if (verbose)
|
||||
{
|
||||
char signame[MAX (SIG2STR_MAX, INT_BUFSIZE_BOUND (int))];
|
||||
if (sig == 0 || sig2str (sig, signame) != 0)
|
||||
snprintf (signame, sizeof signame, "%d", sig);
|
||||
error (0, 0, _("sending signal %s to command %s"),
|
||||
signame, quote (command));
|
||||
}
|
||||
send_sig (monitored_pid, sig);
|
||||
|
||||
/* The normal case is the job has remained in our
|
||||
@@ -250,6 +248,19 @@ cleanup (int sig)
|
||||
send_sig (0, SIGCONT);
|
||||
}
|
||||
}
|
||||
|
||||
/* Diagnose only after sending signals, since stderr could block.
|
||||
Note this implies we don't diagnose a --kill-after without -f,
|
||||
but being killed will be apparent in the exit status and
|
||||
corresponding shell diagnostic at least. */
|
||||
if (diagnose)
|
||||
{
|
||||
char signame[MAX (SIG2STR_MAX, INT_BUFSIZE_BOUND (int))];
|
||||
if (sig == 0 || sig2str (sig, signame) != 0)
|
||||
snprintf (signame, sizeof signame, "%d", sig);
|
||||
error (0, 0, _("sending signal %s to command %s"),
|
||||
signame, quote (command));
|
||||
}
|
||||
}
|
||||
else if (monitored_pid == -1)
|
||||
{ /* were in the parent, so let it continue to exit below. */
|
||||
|
||||
Vendored
+2
-1
@@ -89,7 +89,8 @@ env_ignore_delay_()
|
||||
local delay="$1"
|
||||
|
||||
# The first 'env' is just to ensure timeout is not a shell built-in.
|
||||
env timeout --verbose --kill-after=.1 --signal=$timeout_sig $delay \
|
||||
env timeout --foreground --verbose --kill-after=.1 \
|
||||
--signal=$timeout_sig $delay \
|
||||
env $env_opt sleep 10 > /dev/null 2>outt
|
||||
# check only the first two lines from stderr, which are printed by timeout.
|
||||
# (operating systems might add more messages, like "killed").
|
||||
|
||||
@@ -71,7 +71,7 @@ timeout: sending signal 0 to command 'sleep'
|
||||
timeout: sending signal KILL to command 'sleep'
|
||||
EOF
|
||||
for opt in -v --verbose; do
|
||||
timeout $opt -s0 -k .1 .1 sleep 10 2> errt
|
||||
timeout --foreground $opt -s0 -k .1 .1 sleep 10 2> errt
|
||||
sed '/^Killed/d' < errt > err || framework_failure_
|
||||
compare exp err || fail=1
|
||||
done
|
||||
@@ -81,10 +81,38 @@ done
|
||||
# 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 124 > timeout.exp || framework_failure_
|
||||
{ timeout -v .1 sleep 10 2>&1; echo $? >timeout.status; } | :
|
||||
compare timeout.exp timeout.status || fail=1
|
||||
for opt in '' '--foreground'; do
|
||||
{ timeout -s KILL 10 timeout $opt -v .1 sleep 10 2>&1
|
||||
echo $? >timeout.status; } | :
|
||||
compare timeout.exp timeout.status || fail=1
|
||||
done
|
||||
# Ensure we don't catch/propagate ignored signals
|
||||
(trap '' PIPE && timeout 10 yes |:) 2>&1 |
|
||||
grep 'Broken pipe' >/dev/null || fail=1
|
||||
|
||||
# A diagnostic exceeding the file-size limit must not loop on SIGXFSZ.
|
||||
if (trap - XFSZ && ulimit -f 0) 2>/dev/null; then
|
||||
returns_ 124 timeout -s KILL 10 sh -c '
|
||||
trap - XFSZ
|
||||
ulimit -f 0 || exit 1
|
||||
exec timeout --foreground -v .1 sleep 10 2>err
|
||||
' || fail=1
|
||||
fi
|
||||
|
||||
# Signal the command and its group before writing to a blocked stderr.
|
||||
file_seen() { sleep "$1"; test -s "$file"; }
|
||||
file=term.seen
|
||||
timeout -v .1 sh -c 'trap "echo 1 >term.seen" TERM; yes >&2' 2>&1 |
|
||||
{ retry_delay_ file_seen .1 6 || touch term.missed; }
|
||||
test -e term.missed && fail=1
|
||||
|
||||
# A group-wide SIGKILL must not wait for the verbose diagnostic either.
|
||||
file=kill.status
|
||||
{ timeout -v -s KILL .1 yes 2>&1
|
||||
echo $? >kill.status; } |
|
||||
{ retry_delay_ file_seen .1 6 || touch kill.missed; }
|
||||
test -e kill.missed && fail=1
|
||||
echo 137 > kill.exp || framework_failure_
|
||||
compare kill.exp kill.status || fail=1
|
||||
|
||||
Exit $fail
|
||||
Reference in new issue
Block a user