]> granicus.if.org Git - clang/commitdiff
[PCH] Be conservative and check all the files the PCH references to see if
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>
Wed, 1 Jun 2011 05:43:53 +0000 (05:43 +0000)
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>
Wed, 1 Jun 2011 05:43:53 +0000 (05:43 +0000)
a file was modified since the time the PCH was created.

The parser is not fit to deal with stale PCHs, too many invariants do not hold up. rdar://9530587.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@132389 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Serialization/ASTReader.h
lib/Serialization/ASTReader.cpp
test/PCH/modified-header-error.c [new file with mode: 0644]

index 244503a82aa4b7bde75c12b785b947ddbc53d4a5..6d3a0c9bd04d1531849320064e69012bf4ca3523 100644 (file)
@@ -789,6 +789,10 @@ private:
   /// \brief Reads a statement from the specified cursor.
   Stmt *ReadStmtFromStream(PerFileData &F);
 
+  /// \brief Get a FileEntry out of stored-in-PCH filename, making sure we take
+  /// into account all the necessary relocations.
+  const FileEntry *getFileEntry(llvm::StringRef filename);
+
   void MaybeAddSystemRootToFilename(std::string &Filename);
 
   ASTReadResult ReadASTCore(llvm::StringRef FileName, ASTFileType Type);
@@ -888,6 +892,10 @@ public:
   /// name.
   ASTReadResult ReadAST(const std::string &FileName, ASTFileType Type);
 
+  /// \brief Checks that no file that is stored in PCH is out-of-sync with
+  /// the actual file in the file system.
+  ASTReadResult validateFileEntries();
+
   /// \brief Set the AST callbacks listener.
   void setListener(ASTReaderListener *listener) {
     Listener.reset(listener);
index 240df2cde8d8662a12123d84ec344bee63bded5a..8b99fc7dfe6e2b4332fd5f70c4a4c9064602834d 100644 (file)
@@ -1842,6 +1842,22 @@ MacroDefinition *ASTReader::getMacroDefinition(MacroID ID) {
   return MacroDefinitionsLoaded[ID - 1];
 }
 
+const FileEntry *ASTReader::getFileEntry(llvm::StringRef filenameStrRef) {
+  std::string Filename = filenameStrRef;
+  MaybeAddSystemRootToFilename(Filename);
+  const FileEntry *File = FileMgr.getFile(Filename);
+  if (File == 0 && !OriginalDir.empty() && !CurrentDir.empty() &&
+      OriginalDir != CurrentDir) {
+    std::string resolved = resolveFileRelativeToOriginalDir(Filename,
+                                                            OriginalDir,
+                                                            CurrentDir);
+    if (!resolved.empty())
+      File = FileMgr.getFile(resolved);
+  }
+
+  return File;
+}
+
 /// \brief If we are loading a relocatable PCH file, and the filename is
 /// not an absolute path, add the system root to the beginning of the file
 /// name.
@@ -2351,6 +2367,79 @@ ASTReader::ReadASTBlock(PerFileData &F) {
   return Failure;
 }
 
+ASTReader::ASTReadResult ASTReader::validateFileEntries() {
+  for (unsigned CI = 0, CN = Chain.size(); CI != CN; ++CI) {
+    PerFileData *F = Chain[CI];
+    llvm::BitstreamCursor &SLocEntryCursor = F->SLocEntryCursor;
+
+    for (unsigned i = 0, e = F->LocalNumSLocEntries; i != e; ++i) {
+      SLocEntryCursor.JumpToBit(F->SLocOffsets[i]);
+      unsigned Code = SLocEntryCursor.ReadCode();
+      if (Code == llvm::bitc::END_BLOCK ||
+          Code == llvm::bitc::ENTER_SUBBLOCK ||
+          Code == llvm::bitc::DEFINE_ABBREV) {
+        Error("incorrectly-formatted source location entry in AST file");
+        return Failure;
+      }
+  
+      RecordData Record;
+      const char *BlobStart;
+      unsigned BlobLen;
+      switch (SLocEntryCursor.ReadRecord(Code, Record, &BlobStart, &BlobLen)) {
+      default:
+        Error("incorrectly-formatted source location entry in AST file");
+        return Failure;
+  
+      case SM_SLOC_FILE_ENTRY: {
+        llvm::StringRef Filename(BlobStart, BlobLen);
+        const FileEntry *File = getFileEntry(Filename);
+
+        if (File == 0) {
+          std::string ErrorStr = "could not find file '";
+          ErrorStr += Filename;
+          ErrorStr += "' referenced by AST file";
+          Error(ErrorStr.c_str());
+          return IgnorePCH;
+        }
+  
+        if (Record.size() < 6) {
+          Error("source location entry is incorrect");
+          return Failure;
+        }
+
+        // The stat info from the FileEntry came from the cached stat
+        // info of the PCH, so we cannot trust it.
+        struct stat StatBuf;
+        if (::stat(File->getName(), &StatBuf) != 0) {
+          StatBuf.st_size = File->getSize();
+          StatBuf.st_mtime = File->getModificationTime();
+        }
+
+        if (((off_t)Record[4] != StatBuf.st_size
+#if !defined(LLVM_ON_WIN32)
+            // In our regression testing, the Windows file system seems to
+            // have inconsistent modification times that sometimes
+            // erroneously trigger this error-handling path.
+             || (time_t)Record[5] != StatBuf.st_mtime
+#endif
+            )) {
+          Error(diag::err_fe_pch_file_modified, Filename);
+          return IgnorePCH;
+        }
+
+        break;
+      }
+
+      case SM_SLOC_BUFFER_ENTRY:
+      case SM_SLOC_INSTANTIATION_ENTRY:
+        break;
+      }
+    }
+  }
+
+  return Success;
+}
+
 ASTReader::ASTReadResult ASTReader::ReadAST(const std::string &FileName,
                                             ASTFileType Type) {
   switch(ReadASTCore(FileName, Type)) {
@@ -2361,6 +2450,14 @@ ASTReader::ASTReadResult ASTReader::ReadAST(const std::string &FileName,
 
   // Here comes stuff that we only do once the entire chain is loaded.
 
+  if (!DisableValidation) {
+    switch(validateFileEntries()) {
+    case Failure: return Failure;
+    case IgnorePCH: return IgnorePCH;
+    case Success: break;
+    }
+  }
+
   // Allocate space for loaded slocentries, identifiers, decls and types.
   unsigned TotalNumIdentifiers = 0, TotalNumTypes = 0, TotalNumDecls = 0,
            TotalNumPreallocatedPreprocessingEntities = 0, TotalNumMacroDefs = 0,
diff --git a/test/PCH/modified-header-error.c b/test/PCH/modified-header-error.c
new file mode 100644 (file)
index 0000000..6335fb1
--- /dev/null
@@ -0,0 +1,11 @@
+// RUN: mkdir -p %t.dir
+// RUN: echo '#include "header2.h"' > %t.dir/header1.h
+// RUN: echo > %t.dir/header2.h
+// RUN: cp %s %t.dir/t.c
+// RUN: %clang_cc1 -x c-header %t.dir/header1.h -emit-pch -o %t.pch
+// RUN: echo >> %t.dir/header2.h
+// RUN: %clang_cc1 %t.dir/t.c -include-pch %t.pch -fsyntax-only 2>&1 | FileCheck %s
+
+#include "header2.h"
+
+// CHECK: fatal error: file {{.*}} has been modified since the precompiled header was built