aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>2011-06-01 05:43:53 +0000
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>2011-06-01 05:43:53 +0000
commit460132d35ce36d8dc9701035d97f23d13dcb91f7 (patch)
tree6a8dd52a76665328ae2da07936497477469f3600
parent9f20804216ef49f4c904bce812b390da33c297c9 (diff)
downloadllvm-460132d35ce36d8dc9701035d97f23d13dcb91f7.zip
llvm-460132d35ce36d8dc9701035d97f23d13dcb91f7.tar.gz
llvm-460132d35ce36d8dc9701035d97f23d13dcb91f7.tar.bz2
[PCH] Be conservative and check all the files the PCH references to see if
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. llvm-svn: 132389
-rw-r--r--clang/include/clang/Serialization/ASTReader.h8
-rw-r--r--clang/lib/Serialization/ASTReader.cpp97
-rw-r--r--clang/test/PCH/modified-header-error.c11
3 files changed, 116 insertions, 0 deletions
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index 244503a..6d3a0c9 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -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);
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 240df2c..8b99fc7 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -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/clang/test/PCH/modified-header-error.c b/clang/test/PCH/modified-header-error.c
new file mode 100644
index 0000000..6335fb1
--- /dev/null
+++ b/clang/test/PCH/modified-header-error.c
@@ -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