Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix fatal error reported on Windows when stdout fed to process which terminates before all output written #48672

Closed
jh7370 opened this issue Feb 23, 2021 · 3 comments
Labels
bugzilla Issues migrated from bugzilla llvm:support

Comments

@jh7370
Copy link
Collaborator

jh7370 commented Feb 23, 2021

Bugzilla Link 49328
Version trunk
OS Windows NT
CC @MaskRay

Extended Description

I'm filing this against llvm-objdump, as that was the tool where this was first noticed, but the issue is likely generic to most tools that produce stdout output that might be redirected to the stdin of another process. If the other process terminates before reading all the output (for example a tool like "head"), then a crash in llvm-objdump is seen.

Example head.c:
#include <stdio.h>

static char buf[10240];
extern int atoi(const char *);

int main(int argc, char **argv)
{
int i, n = atoi(argv[1]);
for (i=0; i<n; i++) {
fgets(buf, sizeof(buf), stdin);
puts(buf);
}
return 0;
}

C:\Work>llvm-objdump -d test.elf | head 10

LLVM ERROR: IO failure on output stream: invalid argument
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace.

Sometimes the backtrace doesn't appear after this, sometimes it does.

I debugged the behaviour. The error is reported because an error is stored in the raw_fd_ostream for stdout, which hasn't been cleared, and as such a report_fatal_error is triggered in the raw_fd_ostream's destructor. The error is originally generated during a call to write_impl, and actually comes from the call to ::write in that function. ::write sets errno to EINVAL, which is the value stored in the stream's EC member.

After digging into the behaviour of ::write, I discovered the actual error comes from an underlying call to WriteFile, after which the GetLastError() return is ERROR_NO_DATA. Later, this value is translated into EINVAL for placing in errno, which is not a particularly useful value for errno, as it could represent a number of different failures. ERROR_NO_DATA on the other hand is somewhat clearer - it appears to signal that the reader end of the pipe has been closed when the writer attempts to write to it. I have searched around, and the documentation for this behaviour is virtually non-existent, although there are others who have confirmed that the error we see is seen by them in the same situation too.

Fortunately, ERROR_NO_DATA is still returned by GetLastError by the time ::write returns. As such, we could detect this specific error value if errno is EINVAL and do something different to what we do now. As it seems not unreasonable for a process to hang up after it has received all the data it wants, it might make sense to throw away all subsequent output if that error has ever been detected. It doesn't seem right that we get a fatal error (i.e. a crash) instead.

@MaskRay
Copy link
Member

MaskRay commented Feb 23, 2021

I have seen this on Linux as well (likely from llvm-objdump -d %t | FileCheck %s where FileCheck reports an error and quits (so the read end of the pipe is closed)) but do not know how to reproduce it reliably.

InitLLVM installs a signal handler for SIGPIPE (https://reviews.llvm.org/D70277). The handler (DefaultOneShotPipeSignalHandler).

If the SIGPIPE handler somehow returns, ::write will return -1 with errno set to EPIPE.

Quote POSIX

Broken pipe. A write was attempted on a socket, pipe, or FIFO for which there is no process to read the data.

XRAT B.2 General Information: This condition normally generates the signal SIGPIPE; the error is returned if the generation of the signal is suppressed or the signal does not terminate the
process.

This can probably cause the following code path.

% git diff -U4
diff --git i/llvm/lib/Support/raw_ostream.cpp w/llvm/lib/Support/raw_ostream.cpp
index 8f10d136bc38..75959ec1bbdf 100644
--- i/llvm/lib/Support/raw_ostream.cpp
+++ w/llvm/lib/Support/raw_ostream.cpp
@@ -679,9 +679,9 @@ raw_fd_ostream::~raw_fd_ostream() {
// If there are any pending errors, report them now. Clients wishing
// to avoid report_fatal_error calls should check for errors with
// has_error() and clear the error flag with clear_error() before
// destructing raw_ostream objects which may have errors.

  • if (has_error())
  • if (has_error() && error().value() != (int)std::errc::broken_pipe)
    report_fatal_error("IO failure on output stream: " + error().message(),
    /gen_crash_diag=/false);
    }

@jh7370
Copy link
Collaborator Author

jh7370 commented Feb 24, 2021

It's worth emphasising that the suggestion you've made won't fix the Windows side without other changes (because it doesn't use EPIPE in this instance), but we could always modify the error recording code in write_impl to detect ERROR_NO_DATA and convert EINVAL to EPIPE.

My instinct however, is that we shouldn't consider a broken pipe an error here at all, i.e. has_errors() should return false, as that would ensure other tools that actually check for errors don't see any. I don't have a strong opinion on it either way though, so could easily be swayed by an argument to do something different.

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 11, 2021
@j0le
Copy link

j0le commented Feb 10, 2022

I had a similar crash when piping the help page from clang to more using cmd.exe as shell, but quitting from more by pressing q:

C:\>"C:\Program Files\LLVM\bin\clang.exe" --help | "C:\Windows\System32\more.com"
OVERVIEW: clang LLVM compiler

USAGE: clang.exe [options] file...

OPTIONS:
  -###                    Print (but do not run) the commands to run for this compilation
  --analyzer-output <value>
                          Static analyzer report output format (html|plist|plist-multi-file|plist-html|sarif|text).
  --analyze               Run the static analyzer
  -arcmt-migrate-emit-errors
LLVM ERROR: IO failure on output stream: invalid argument
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace, preprocessed source, and associated run script.
 #0 0x00007ff6db9b1246 C:\Program Files\LLVM\bin\clang.exe 0x1ae1246 (C:\Program Files\LLVM\bin\clang.exe+0x1ae1246)
 #1 0x00007ff6db9b1246
 #2 0x00007ff6db9b1246 (C:\Program Files\LLVM\bin\clang.exe+0x1ae1246)
 #3 0x00007ffb4067caad C:\Program Files\LLVM\bin\clang.exe 0x1adef52 C:\Program Files\LLVM\bin\clang.exe 0x1adef7a
 #4 0x00007ffb4067caad C:\Program Files\LLVM\bin\clang.exe 0x1ac7012 (C:\Windows\System32\ucrtbase.dll+0x6caad)
 #5 0x00007ffb4067caad
 #6 0x00007ffb4067caad (C:\Windows\System32\ucrtbase.dll+0x6caad)
 #7 0x00007ffb4067dab1 (C:\Windows\System32\ucrtbase.dll+0x6dab1)
 #8 0x00007ff6db9aef52 (C:\Program Files\LLVM\bin\clang.exe+0x1adef52)
 #9 0x00007ff6db9aef7a (C:\Program Files\LLVM\bin\clang.exe+0x1adef7a)
#10 0x00007ff6db997012 (C:\Program Files\LLVM\bin\clang.exe+0x1ac7012)
#11 0x00007ffb40629d26 C:\Program Files\LLVM\bin\clang.exe 0x3d8ea1f (C:\Windows\System32\ucrtbase.dll+0x19d26)
#12 0x00007ffb40629d26
#13 0x00007ffb40629d26 (C:\Windows\System32\ucrtbase.dll+0x19d26)
0x00007FF6DB9B1246 (0x00009F804F7B7B98 0x00007FF6DB9B20DD 0x0000000000000016 0x0000000000000000)
0x00007FFB4067CAAD (0x00000035E718FA01 0x0000000000000000 0x0000000000000000 0x00000035E718FA20), raise() + 0x1DD bytes(s)
0x00007FFB4067DAB1 (0x0000000000000003 0x0000003500000003 0x0000000000000000 0x00007FFB406214CB), abort() + 0x31 bytes(s)
0x00007FF6DB9AEF52 (0x000001B008F0D6A0 0x00000035E718FB90 0x00000035E718FB70 0x0000000000000000)
0x00007FF6DB9AEF7A (0x00009F804F7B79F8 0x00007FF6DB3A6EF9 0x00000000000000E8 0x00007FFB40629F66)
0x00007FF6DB997012 (0x00009F804F7B7978 0x00007FF6DF63B8F0 0x00000000000000E8 0x00007FFB40629F66)
0x00007FFB40629D26 (0x00000035E718FD00 0x00000035E718FC98 0x00000035E718FD30 0x00007FFB406FBC20), _execute_onexit_table() + 0x156 bytes(s)
0x00007FFB40629C4B (0x00000035E718FD18 0x00007FFB406FBC20 0x2E676E616C635C6E 0x00000035E718FC88), _execute_onexit_table() + 0x7B bytes(s)
0x00007FFB40629C04 (0x00007FFB406FC038 0x00007FFB00000002 0x00007FF600000002 0x00000035E718FC80), _execute_onexit_table() + 0x34 bytes(s)
0x00007FFB406287D2 (0x0000000000000000 0x00000035E718FD30 0x0000000000000000 0x0000000000000000), exit() + 0x142 bytes(s)
0x00007FFB4062875B (0x0000000000000000 0x0000000000000000 0x000001B008EE4010 0x00000035E718FD10), exit() + 0xCB bytes(s)
0x00007FFB406286FE (0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000), exit() + 0x6E bytes(s)
0x00007FF6DDC5EA1F (0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000)
0x00007FFB41E27C24 (0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000), BaseThreadInitThunk() + 0x14 bytes(s)
0x00007FFB428ED721 (0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000), RtlUserThreadStart() + 0x21 bytes(s)

C:\>ver

Microsoft Windows [Version 10.0.18363.2037]

C:\>"C:\Program Files\LLVM\bin\clang.exe" --version
clang version 12.0.1
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files\LLVM\bin

@nga888 nga888 closed this as completed in 0b704d9 Feb 9, 2023
llvmbot pushed a commit to llvm/llvm-project-release-prs that referenced this issue Feb 9, 2023
Prevent errors and crash dumps for broken pipes on Windows.

Fixes: llvm/llvm-project#48672

Differential Revision: https://reviews.llvm.org/D142224

(cherry picked from commit 0b704d9)
CarlosAlbertoEnciso pushed a commit to SNSystems/llvm-debuginfo-analyzer that referenced this issue Feb 10, 2023
Prevent errors and crash dumps for broken pipes on Windows.

Fixes: llvm/llvm-project#48672

Differential Revision: https://reviews.llvm.org/D142224
tru pushed a commit to llvm/llvm-project-release-prs that referenced this issue Feb 13, 2023
Prevent errors and crash dumps for broken pipes on Windows.

Fixes: llvm/llvm-project#48672

Differential Revision: https://reviews.llvm.org/D142224

(cherry picked from commit 0b704d9)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugzilla Issues migrated from bugzilla llvm:support
Projects
None yet
Development

No branches or pull requests

4 participants