On 01/12/2025 22:14, Bruno Haible via GNU coreutils General Discussion wrote:
Hi,

On Ubuntu 24.04, the unit test that was added in commit
d0a51c614def6cf4f39fafe8ca7dcef925c4a503 fails when clang with ASAN and UBSAN
is used: The file timeout.status is expected to contain the number 125,
but it contains the number 124.

How to reproduce:

1. Set the environment variables

CC="$CLANG_INST_DIR/bin/clang -Wl,-rpath,$CLANG_INST_DIR/lib"

CC="$CC 
-fsanitize=address,undefined,signed-integer-overflow,shift,integer-divide-by-zero 
-fno-sanitize-recover=undefined"; export CC
CFLAGS="-O0 -fno-omit-frame-pointer -ggdb"; export CFLAGS

ASAN_OPTIONS="detect_leaks=0 abort_on_error=1 allocator_may_return_null=1"
export ASAN_OPTIONS

2. Configure and build coreutils.

3.
$ cd src
$ { ./timeout -v .1 sleep 10 2>&1; echo $? >timeout.status; } | :
$ cat timeout.status
124


Ah right, the close_out() atexit handlerdoesn't call fclose(stderr) if 
SANITIZE_ADDRESS is defined,
"as sanitizers may report to stderr after this function returns".

Otherwise timeout(1) would have noticed the EPIPE error
trying to write to stderr, and exit with 125 rather than 124.

The attached updates timeout(1) to exit with status 124 in either case,
as it would be generally bad for 125 to hide the fact that we
have actually timed out the command.

I'll apply the attached a little later.

cheers,
Padraig
From 86061ddbbdb4194362ede72f9900297d8d31d051 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]>
Date: Tue, 2 Dec 2025 17:15:06 +0000
Subject: [PATCH] timeout: prioritize "timed out" exit status

* src/timeout.c (cleanup): Reset the default exit status
to EXIT_TIMEDOUT, if we have in fact received an ALARM.
Otherwise we would exit with status EXIT_CANCELED if
there was an issue writing --verbose output for example.
This also ensures a consistent exit status with ASAN enabled,
as with ASAN stderr is not explicitly closed by gnulib's
close_stdout handler.
---
 src/timeout.c            | 6 ++++++
 tests/timeout/timeout.sh | 2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/timeout.c b/src/timeout.c
index 7634323d4..3303b5778 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -206,6 +206,12 @@ cleanup (int sig)
   if (sig == SIGALRM)
     {
       timed_out = 1;
+      /* In case there is an issue with close_stdout,
+         update to a more accurate default exit status.
+         For example we might get failed writes with -v with:
+           timeout -v 1 sleep 10 2>&1 | :
+      */
+      initialize_exit_failure (EXIT_TIMEDOUT);
       sig = term_signal;
     }
   if (0 < monitored_pid)
diff --git a/tests/timeout/timeout.sh b/tests/timeout/timeout.sh
index 36dcba2d0..c7d8a2906 100755
--- a/tests/timeout/timeout.sh
+++ b/tests/timeout/timeout.sh
@@ -75,7 +75,7 @@ done
 # 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_
+echo 124 > timeout.exp || framework_failure_
 { timeout -v .1 sleep 10 2>&1; echo $? >timeout.status; } | :
 compare timeout.exp timeout.status || fail=1
 # Ensure we don't catch/propagate ignored signals
-- 
2.51.1

Reply via email to