From: Argyrios Kyrtzidis Date: Wed, 1 Jun 2011 05:43:53 +0000 (+0000) Subject: [PCH] Be conservative and check all the files the PCH references to see if X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b68ffb107a86f5e3851e8108c712b64dd16ba258;p=clang [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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@132389 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 244503a82a..6d3a0c9bd0 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/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/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 240df2cde8..8b99fc7dfe 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/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/test/PCH/modified-header-error.c b/test/PCH/modified-header-error.c new file mode 100644 index 0000000000..6335fb1b45 --- /dev/null +++ b/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