[PATCH] D13128: Fix backend crash on multiple close of stdout.

Alexey Bataev via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 24 05:20:57 PDT 2015


ABataev created this revision.
ABataev added a reviewer: chandlerc.
ABataev added a subscriber: cfe-commits.

If stdout is used as an output file for several outputs (like dep file, output file, etc.) , it causes a crash in llvm::raw_fd_ostream::~raw_fd_ostream(), when destructor tries to close file descriptor, which already is closed in another stream.
Here is the part of the code where it happens:
```
raw_fd_ostream::~raw_fd_ostream() {
  if (FD >= 0) {
    flush();
    if (ShouldClose && sys::Process::SafelyCloseFileDescriptor(FD))
      error_detected();
  }
...
  if (has_error())
    report_fatal_error("IO failure on output stream.", /*GenCrashDiag=*/false);
}
```
if stdout is already closed sys::Process::SafelyCloseFileDescriptor() cannot close stdout for the second time and an error flag is set for the stream. It makes the destructor to emit `IO failure on output stream.` fatal error message.
Patch closes such streams manually and then clears (possibly set) error flags for the stream.

http://reviews.llvm.org/D13128

Files:
  include/clang/Frontend/CompilerInstance.h
  lib/Frontend/CompilerInstance.cpp
  test/Frontend/empty.cpp

Index: lib/Frontend/CompilerInstance.cpp
===================================================================
--- lib/Frontend/CompilerInstance.cpp
+++ lib/Frontend/CompilerInstance.cpp
@@ -531,6 +531,12 @@
 void CompilerInstance::clearOutputFiles(bool EraseFiles) {
   for (OutputFile &OF : OutputFiles) {
     // Manually close the stream before we rename it.
+    if (OF.OS && OF.ShouldClose) {
+      // Close the stream and clear error state for stdout.
+      auto *Stream = static_cast<llvm::raw_fd_ostream *>(OF.OS.get());
+      Stream->close();
+      Stream->clear_error();
+    }
     OF.OS.reset();
 
     if (!OF.TempFilename.empty()) {
@@ -555,6 +561,11 @@
 
   }
   OutputFiles.clear();
+  if (NonSeekStream) {
+    // Close the stream and clear error state for stdout.
+    NonSeekStream->close();
+    NonSeekStream->clear_error();
+  }
   NonSeekStream.reset();
 }
 
@@ -593,7 +604,8 @@
   // Add the output file -- but don't try to remove "-", since this means we are
   // using stdin.
   addOutputFile(OutputFile((OutputPathName != "-") ? OutputPathName : "",
-                           TempPathName, std::move(OS)));
+                           TempPathName, std::move(OS),
+                           OutputPathName == "-" && !Binary));
 
   return Ret;
 }
Index: include/clang/Frontend/CompilerInstance.h
===================================================================
--- include/clang/Frontend/CompilerInstance.h
+++ include/clang/Frontend/CompilerInstance.h
@@ -153,14 +153,17 @@
     std::string Filename;
     std::string TempFilename;
     std::unique_ptr<raw_ostream> OS;
+    /// \brief true if the file stream should be closed before to be destroyed.
+    bool ShouldClose;
 
     OutputFile(std::string filename, std::string tempFilename,
-               std::unique_ptr<raw_ostream> OS)
+               std::unique_ptr<raw_ostream> OS, bool ShouldClose = false)
         : Filename(std::move(filename)), TempFilename(std::move(tempFilename)),
-          OS(std::move(OS)) {}
+          OS(std::move(OS)), ShouldClose(ShouldClose) {}
     OutputFile(OutputFile &&O)
         : Filename(std::move(O.Filename)),
-          TempFilename(std::move(O.TempFilename)), OS(std::move(O.OS)) {}
+          TempFilename(std::move(O.TempFilename)), OS(std::move(O.OS)),
+          ShouldClose(O.ShouldClose) {}
   };
 
   /// If the output doesn't support seeking (terminal, pipe). we switch
Index: test/Frontend/empty.cpp
===================================================================
--- test/Frontend/empty.cpp
+++ test/Frontend/empty.cpp
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -verify %s -x c++ -triple x86_64-unknown-linux-gnu -dependency-file - -MT - -emit-llvm -o - | FileCheck %s
+// Check that no additional error messages are generated.
+int main() {
+  return; // expected-error {{non-void function 'main' should return a value}}
+}
+// CHECK: -:


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D13128.35609.patch
Type: text/x-patch
Size: 2887 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20150924/a9c49a15/attachment.bin>


More information about the cfe-commits mailing list