Skip to content

Commit

Permalink
Fix use-after-free and spurious error during module load
Browse files Browse the repository at this point in the history
FileManager::invalidateCache is not safe to call when there may be
existing references to the file. What module load failure needs is
to refresh so stale stat() info isn't stored.

This may be the last user of invalidateCache; I'll take a look and
remove it if possible in a future commit.

This caused a use-after-free error as well as a spurious error message
that a module was "found in both 'X.pcm' and 'X.pcm'" in some cases.

llvm-svn: 209138
  • Loading branch information
benlangmuir committed May 19, 2014
1 parent d71b6df commit ca39214
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 2 deletions.
15 changes: 13 additions & 2 deletions clang/lib/Serialization/ModuleManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,20 @@ void ModuleManager::removeModules(ModuleIterator first, ModuleIterator last,

// Delete the modules and erase them from the various structures.
for (ModuleIterator victim = first; victim != last; ++victim) {
Modules.erase((*victim)->File);
const FileEntry *F = (*victim)->File;
Modules.erase(F);

// Refresh the stat() information for the module file so stale information
// doesn't get stored accidentally.
vfs::Status UpdatedStat;
if (FileMgr.getNoncachedStatValue(F->getName(), UpdatedStat)) {
llvm::report_fatal_error(Twine("module file '") + F->getName() +
"' removed after it has been used");
} else {
FileMgr.modifyFileEntry(const_cast<FileEntry *>(F), UpdatedStat.getSize(),
UpdatedStat.getLastModificationTime().toEpochTime());
}

FileMgr.invalidateCache((*victim)->File);
if (modMap) {
StringRef ModuleName = (*victim)->ModuleName;
if (Module *mod = modMap->findModule(ModuleName)) {
Expand Down
25 changes: 25 additions & 0 deletions clang/test/Modules/load-after-failure.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// REQUIRES: shell
// RUN: rm -rf %t
// RUN: mkdir -p %t

// RUN: echo '@import B;' > %t/A.h
// RUN: echo '@import C;' > %t/B.h
// RUN: echo '@import D;' >> %t/B.h
// RUN: echo '// C.h' > %t/C.h
// RUN: echo '// D.h' > %t/D.h
// RUN: echo 'module A { header "A.h" }' > %t/module.modulemap
// RUN: echo 'module B { header "B.h" }' >> %t/module.modulemap
// RUN: echo 'module C { header "C.h" }' >> %t/module.modulemap
// RUN: echo 'module D { header "D.h" }' >> %t/module.modulemap

// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -I %t %s -verify
// RUN: echo " " >> %t/D.h
// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -I %t %s -verify
// expected-no-diagnostics


@import C;
@import A;
@import C;
// When compiling A, C will be be loaded then removed when D fails. Ensure
// this does not cause problems importing C again later.

0 comments on commit ca39214

Please sign in to comment.