Skip to content

Commit 57f2944

Browse files
committed
Add A Reproducer For rdar://48443680
The ModuleManager in Clang maintains a mapping from FileEntry nodes to ModuleFile data. Because FileEntry nodes are unique'd by underlying inode number, an attacker can craft a collision by ensuring that two different PCMs share an inode number. This failure can be encountered with some regularity in a highly concurrent compilation session on filesystems that use Orlov's Algorithm to allocate inode numbers (like ext4 on Linux). Here, we use a much simpler algorithm that forgets that PCMs have distinct inode numbers. A future version of clang will increase the entropy of the key value to take into account the mod time and size, which we also conveniently map to 0 to throw it off our trail.
1 parent 457b999 commit 57f2944

File tree

1 file changed

+106
-1
lines changed

1 file changed

+106
-1
lines changed

unittests/ClangImporter/ClangImporterTests.cpp

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
#include "swift/Basic/SourceManager.h"
77
#include "swift/ClangImporter/ClangImporter.h"
88
#include "swift/ClangImporter/ClangImporterOptions.h"
9+
#include "clang/Basic/FileManager.h"
10+
#include "clang/Basic/FileSystemOptions.h"
11+
#include "clang/Basic/FileSystemStatCache.h"
12+
#include "clang/Frontend/CompilerInstance.h"
913
#include "llvm/Support/FileSystem.h"
1014
#include "llvm/Support/Path.h"
1115
#include "llvm/Support/raw_ostream.h"
@@ -54,7 +58,6 @@ TEST(ClangImporterTest, emitPCHInMemory) {
5458
// Create the includes.
5559
std::string include = createFilename(temp, "include");
5660
ASSERT_FALSE(llvm::sys::fs::create_directory(include));
57-
options.ExtraArgs.emplace_back("-nosysteminc");
5861
options.ExtraArgs.emplace_back((llvm::Twine("-I") + include).str());
5962
ASSERT_FALSE(emitFileWithContents(include, "module.modulemap",
6063
"module A {\n"
@@ -88,3 +91,105 @@ TEST(ClangImporterTest, emitPCHInMemory) {
8891
ASSERT_FALSE(emitFileWithContents(PCH, "garbage"));
8992
ASSERT_TRUE(importer->canReadPCH(PCH));
9093
}
94+
95+
class ForgetfulStatCache : public clang::FileSystemStatCache {
96+
private:
97+
std::unique_ptr<clang::MemorizeStatCalls> RealStatCache;
98+
99+
public:
100+
ForgetfulStatCache() {
101+
RealStatCache = std::make_unique<clang::MemorizeStatCalls>();
102+
}
103+
~ForgetfulStatCache() = default;
104+
105+
std::error_code getStat(StringRef Path, llvm::vfs::Status &Status,
106+
bool isFile,
107+
std::unique_ptr<llvm::vfs::File> *F,
108+
llvm::vfs::FileSystem &FS) override {
109+
if (llvm::sys::path::extension(Path) == ".pcm") {
110+
const auto UID = llvm::sys::fs::UniqueID(1, std::numeric_limits<uint64_t>::max());
111+
Status = llvm::vfs::Status(Path, UID,
112+
/*MTime*/{}, /*User*/0, /*Group*/0,
113+
/*Size*/0,
114+
llvm::sys::fs::file_type::regular_file,
115+
llvm::sys::fs::perms::all_all);
116+
return std::error_code();
117+
}
118+
119+
return clang::FileSystemStatCache::get(Path, Status, isFile, F,
120+
RealStatCache.get(), FS);
121+
}
122+
};
123+
124+
TEST(ClangImporterTest, missingSubmodule) {
125+
// Create a temporary cache on disk and clean it up at the end.
126+
ClangImporterOptions options;
127+
SmallString<256> temp;
128+
ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory(
129+
"ClangImporterTest.missingSubmodule", temp));
130+
SWIFT_DEFER { llvm::sys::fs::remove_directories(temp); };
131+
132+
// Create a cache subdirectory for the modules and PCH.
133+
std::string cache = createFilename(temp, "cache");
134+
ASSERT_FALSE(llvm::sys::fs::create_directory(cache));
135+
options.ModuleCachePath = cache;
136+
options.PrecompiledHeaderOutputDir = cache;
137+
138+
// Create the includes.
139+
std::string include1 = createFilename(temp, "include1");
140+
ASSERT_FALSE(llvm::sys::fs::create_directory(include1));
141+
142+
std::string include2 = createFilename(temp, "include2");
143+
ASSERT_FALSE(llvm::sys::fs::create_directory(include2));
144+
145+
options.ExtraArgs.emplace_back((llvm::Twine("-I") + include1).str());
146+
options.ExtraArgs.emplace_back((llvm::Twine("-I") + include2).str());
147+
options.ExtraArgs.emplace_back("-DEXTRA_C_DEFINE=2");
148+
149+
ASSERT_FALSE(emitFileWithContents(include1, "module.modulemap",
150+
"module CLib1 {\n"
151+
" umbrella header \"" + include1 + "/Clib1.h\"\n"
152+
" export * \n"
153+
"}\n"
154+
"module CLib2 {\n"
155+
" umbrella header \"" + include2 + "/Clib2.h\"\n"
156+
" export * \n"
157+
"}\n"));
158+
ASSERT_FALSE(emitFileWithContents(include1, "CLib1.h",
159+
"#if !defined(EXTRA_C_DEFINE) || EXTRA_C_DEFINE != 2\n"
160+
"#error \"unexpected compiler flags\"\n"
161+
"#endif\n"
162+
"void foo(void);\n"));
163+
ASSERT_FALSE(emitFileWithContents(include2, "CLib2.h",
164+
"#if !defined(EXTRA_C_DEFINE) || EXTRA_C_DEFINE != 2\n"
165+
"#error \"unexpected compiler flags\"\n"
166+
"#endif\n"
167+
"void foo(void);\n"));
168+
169+
// Set up the importer.
170+
swift::LangOptions langOpts;
171+
langOpts.Target = llvm::Triple("x86_64", "apple", "darwin");
172+
swift::TypeCheckerOptions typeckOpts;
173+
INITIALIZE_LLVM();
174+
swift::SearchPathOptions searchPathOpts;
175+
swift::SourceManager sourceMgr;
176+
swift::DiagnosticEngine diags(sourceMgr);
177+
std::unique_ptr<ASTContext> context(
178+
ASTContext::get(langOpts, typeckOpts, searchPathOpts, sourceMgr, diags));
179+
auto importer = ClangImporter::create(*context, options);
180+
181+
// Install a stats cache that conveniently "forgets" that PCMs have different
182+
// underlying FileEntry values.
183+
importer->getClangInstance()
184+
.getFileManager()
185+
.setStatCache(std::make_unique<ForgetfulStatCache>());
186+
187+
context->addModuleLoader(std::move(importer));
188+
189+
auto CLib1 = context->getIdentifier("CLib1");
190+
auto CLib2 = context->getIdentifier("CLib2");
191+
// The first load succeeds and primes the ModuleManager with PCM data.
192+
(void)context->getModule({ Located<Identifier>{ CLib1, {} } });
193+
// The second load fails because we collide in the ModuleManager.
194+
ASSERT_TRUE(context->getModule({ Located<Identifier>{ CLib2, {} } }) == nullptr);
195+
}

0 commit comments

Comments
 (0)