[llvm-bugs] [Bug 49328] New: Fix fatal error reported on Windows when stdout fed to process which terminates before all output written

via llvm-bugs llvm-bugs at lists.llvm.org
Tue Feb 23 02:43:15 PST 2021


            Bug ID: 49328
           Summary: Fix fatal error reported on Windows when stdout fed to
                    process which terminates before all output written
           Product: tools
           Version: trunk
          Hardware: PC
                OS: Windows NT
            Status: NEW
          Severity: normal
          Priority: P
         Component: llvm-objdump
          Assignee: unassignedbugs at nondot.org
          Reporter: jh7370.2008 at my.bristol.ac.uk
                CC: llvm-bugs at lists.llvm.org

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);
  return 0;

C:\Work>llvm-objdump -d test.elf | head 10
<a few lines of output>
LLVM ERROR: IO failure on output stream: invalid argument
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash

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.

You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20210223/2305068b/attachment.html>

More information about the llvm-bugs mailing list