Skip to content

Tools should _not_ catch their own SIGPIPE #59037

@kees

Description

@kees

Since commit 51b557a, tools are catching their own SIGPIPE signals. This is wrong: the caller needs to be the one handling these. This is the long-standing behavior for *nix utilities. For example:

$ seq 10000 | head -n1
1

No error generated, but seq is being killed by SIGPIPE:

$ strace seq 10000 | head -n1
...
write(1, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"..., 8192) = 8192
1
write(1, "\n1861\n1862\n1863\n1864\n1865\n1866\n1"..., 4096) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=3503448, si_uid=1000} ---
+++ killed by SIGPIPE +++

It is the caller (the shell, here) that is filtering out SIGPIPE. The tool itself should not catch the signal, and caller are expecting tools to be killed early by SIGPIPE so that needless CPU resources (and time) are not wasted. (For example, when using head, or grep -mN, etc.)

The SIGPIPE handler should be left explicitly untouched, i.e. set to SIG_DFL. If the error is wanted, callers (shells) can be configured to report it:

$ set -o pipefail
$ seq 10000 | head -n1
1
$ echo $?
141

The LLVM tool behavior is incorrect:

$ llvm-objdump -d /bin/true | head -n1

error: write on a pipe with no reader

And we can see that the signal is being caught internally:

$ strace llvm-objdump -d /bin/true | head -n1
...
write(1, "\n/bin/true:\tfile format elf64-x8"..., 95) = 95
write(1, "    1000: f3 0f 1e fa           "..., 48
) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=3503786, si_uid=1000} ---
...
rt_sigaction(SIGPIPE, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa15add7520}, NULL, 8) = 0
...
lseek(2, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
newfstatat(2, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x19), ...}, AT_EMPTY_PATH) = 0
write(2, "error: write on a pipe with no r"..., 38error: write on a pipe with no reader
) = 38
exit_group(74)                          = ?
+++ exited with 74 +++

And errno 74 is also wrong here (EBADMSG). Again, setting pipefail can expose this wrong error too:

$ set -o pipefail
$ llvm-objdump -d /bin/true | head -n1
...
$ echo $?
74

But this errno is not correct. :(

Metadata

Metadata

Assignees

Type

No type

Projects

Status

Done

Relationships

None yet

Development

No branches or pull requests

Issue actions