Skip to content

Commit a23ba4d

Browse files
committed
[clang][modules] Guard against bad -fmodule-file mappings (#132059)
Fix #132059. Providing incorrect mappings via `-fmodule-file=<name>=<path/to/bmi>` can crash the compiler when loading a module that imports an incorrectly mapped module. The crash occurs during AST body deserialization, when the compiler attempts to resolve remappings using the `ModuleFile` from the incorrectly mapped module's BMI file. The cause is an invalid access into an incorrectly loaded `ModuleFile`. This commit fixes the issue by verifying that the mapped BMI files correspond to the mapped-from modules as soon as the module name is read from the BMI's control block, and it errors out if there is a mismatch.
1 parent 2796e41 commit a23ba4d

File tree

7 files changed

+124
-31
lines changed

7 files changed

+124
-31
lines changed

clang/include/clang/Basic/DiagnosticSerializationKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ def err_imported_module_relocated : Error<
9898
def err_module_different_modmap : Error<
9999
"module '%0' %select{uses|does not use}1 additional module map '%2'"
100100
"%select{| not}1 used when the module was built">;
101+
def err_module_mismatch : Error<
102+
"tried loading module '%0' from '%1' but found module '%2' instead">, DefaultFatal;
101103

102104
def err_ast_file_macro_def_undef : Error<
103105
"macro '%0' was %select{defined|undef'd}1 in the AST file '%2' but "

clang/include/clang/Serialization/ASTReader.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,9 @@ class ASTReader
423423
/// configuration.
424424
ConfigurationMismatch,
425425

426+
/// The AST file contains a different module than expected.
427+
ModuleMismatch,
428+
426429
/// The AST file has errors.
427430
HadErrors
428431
};
@@ -1512,11 +1515,13 @@ class ASTReader
15121515
SourceLocation ImportLoc, ModuleFile *ImportedBy,
15131516
SmallVectorImpl<ImportedModule> &Loaded,
15141517
off_t ExpectedSize, time_t ExpectedModTime,
1518+
StringRef ExpectedModuleName,
15151519
ASTFileSignature ExpectedSignature,
15161520
unsigned ClientLoadCapabilities);
15171521
ASTReadResult ReadControlBlock(ModuleFile &F,
15181522
SmallVectorImpl<ImportedModule> &Loaded,
15191523
const ModuleFile *ImportedBy,
1524+
StringRef ExpectedModuleName,
15201525
unsigned ClientLoadCapabilities);
15211526
static ASTReadResult
15221527
ReadOptionsBlock(llvm::BitstreamCursor &Stream, StringRef Filename,
@@ -1819,6 +1824,18 @@ class ASTReader
18191824
unsigned ClientLoadCapabilities,
18201825
ModuleFile **NewLoadedModuleFile = nullptr);
18211826

1827+
/// \overload
1828+
///
1829+
/// Calls the above function and checks if the AST file contains the expected
1830+
/// module. Returns ASTReadResult::Failure on mismatch.
1831+
///
1832+
/// \param ExpectedModuleName The expected name of the new loaded module.
1833+
ASTReadResult ReadAST(StringRef FileName, ModuleKind Type,
1834+
SourceLocation ImportLoc,
1835+
unsigned ClientLoadCapabilities,
1836+
StringRef ExpectedModuleName,
1837+
ModuleFile **NewLoadedModuleFile = nullptr);
1838+
18221839
/// Make the entities in the given module and any of its (non-explicit)
18231840
/// submodules visible to name lookup.
18241841
///

clang/lib/Frontend/ASTUnit.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
887887
case ASTReader::OutOfDate:
888888
case ASTReader::VersionMismatch:
889889
case ASTReader::ConfigurationMismatch:
890+
case ASTReader::ModuleMismatch:
890891
case ASTReader::HadErrors:
891892
AST->getDiagnostics().Report(diag::err_fe_unable_to_load_pch);
892893
return nullptr;

clang/lib/Frontend/ChainedIncludesSource.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ createASTReader(CompilerInstance &CI, StringRef pchFile,
8181
case ASTReader::OutOfDate:
8282
case ASTReader::VersionMismatch:
8383
case ASTReader::ConfigurationMismatch:
84+
case ASTReader::ModuleMismatch:
8485
case ASTReader::HadErrors:
8586
break;
8687
}

clang/lib/Frontend/CompilerInstance.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,7 @@ IntrusiveRefCntPtr<ASTReader> CompilerInstance::createPCHExternalASTSource(
676676
case ASTReader::OutOfDate:
677677
case ASTReader::VersionMismatch:
678678
case ASTReader::ConfigurationMismatch:
679+
case ASTReader::ModuleMismatch:
679680
case ASTReader::HadErrors:
680681
// No suitable PCH file could be found. Return an error.
681682
break;
@@ -1909,13 +1910,12 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST(
19091910
: Source == MS_PrebuiltModulePath
19101911
? 0
19111912
: ASTReader::ARR_ConfigurationMismatch;
1912-
switch (getASTReader()->ReadAST(ModuleFilename,
1913-
Source == MS_PrebuiltModulePath
1914-
? serialization::MK_PrebuiltModule
1915-
: Source == MS_ModuleBuildPragma
1916-
? serialization::MK_ExplicitModule
1917-
: serialization::MK_ImplicitModule,
1918-
ImportLoc, ARRFlags)) {
1913+
switch (getASTReader()->ReadAST(
1914+
ModuleFilename,
1915+
Source == MS_PrebuiltModulePath ? serialization::MK_PrebuiltModule
1916+
: Source == MS_ModuleBuildPragma ? serialization::MK_ExplicitModule
1917+
: serialization::MK_ImplicitModule,
1918+
ImportLoc, ARRFlags, ModuleName)) {
19191919
case ASTReader::Success: {
19201920
if (M)
19211921
return M;
@@ -1952,6 +1952,7 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST(
19521952
// Fall through to error out.
19531953
[[fallthrough]];
19541954
case ASTReader::VersionMismatch:
1955+
case ASTReader::ModuleMismatch:
19551956
case ASTReader::HadErrors:
19561957
ModuleLoader::HadFatalFailure = true;
19571958
// FIXME: The ASTReader will already have complained, but can we shoehorn

clang/lib/Serialization/ASTReader.cpp

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2926,6 +2926,8 @@ static bool isDiagnosedResult(ASTReader::ASTReadResult ARR, unsigned Caps) {
29262926
case ASTReader::VersionMismatch: return !(Caps & ASTReader::ARR_VersionMismatch);
29272927
case ASTReader::ConfigurationMismatch:
29282928
return !(Caps & ASTReader::ARR_ConfigurationMismatch);
2929+
case ASTReader::ModuleMismatch:
2930+
return true;
29292931
case ASTReader::HadErrors: return true;
29302932
case ASTReader::Success: return false;
29312933
}
@@ -3020,11 +3022,10 @@ ASTReader::ASTReadResult ASTReader::ReadOptionsBlock(
30203022
}
30213023
}
30223024

3023-
ASTReader::ASTReadResult
3024-
ASTReader::ReadControlBlock(ModuleFile &F,
3025-
SmallVectorImpl<ImportedModule> &Loaded,
3026-
const ModuleFile *ImportedBy,
3027-
unsigned ClientLoadCapabilities) {
3025+
ASTReader::ASTReadResult ASTReader::ReadControlBlock(
3026+
ModuleFile &F, SmallVectorImpl<ImportedModule> &Loaded,
3027+
const ModuleFile *ImportedBy, StringRef ExpectedModuleName,
3028+
unsigned ClientLoadCapabilities) {
30283029
BitstreamCursor &Stream = F.Stream;
30293030

30303031
if (llvm::Error Err = Stream.EnterSubBlock(CONTROL_BLOCK_ID)) {
@@ -3315,7 +3316,7 @@ ASTReader::ReadControlBlock(ModuleFile &F,
33153316

33163317
// Load the AST file.
33173318
auto Result = ReadASTCore(ImportedFile, ImportedKind, ImportLoc, &F,
3318-
Loaded, StoredSize, StoredModTime,
3319+
Loaded, StoredSize, StoredModTime, ImportedName,
33193320
StoredSignature, Capabilities);
33203321

33213322
// If we diagnosed a problem, produce a backtrace.
@@ -3338,7 +3339,10 @@ ASTReader::ReadControlBlock(ModuleFile &F,
33383339
case OutOfDate: return OutOfDate;
33393340
case VersionMismatch: return VersionMismatch;
33403341
case ConfigurationMismatch: return ConfigurationMismatch;
3341-
case HadErrors: return HadErrors;
3342+
case ModuleMismatch:
3343+
return ModuleMismatch;
3344+
case HadErrors:
3345+
return HadErrors;
33423346
case Success: break;
33433347
}
33443348
break;
@@ -3363,6 +3367,14 @@ ASTReader::ReadControlBlock(ModuleFile &F,
33633367
if (Listener)
33643368
Listener->ReadModuleName(F.ModuleName);
33653369

3370+
// Return if the AST unexpectedly contains a different module
3371+
if (F.Kind == MK_PrebuiltModule && !ExpectedModuleName.empty() &&
3372+
F.ModuleName != ExpectedModuleName) {
3373+
Diag(diag::err_module_mismatch)
3374+
<< ExpectedModuleName << F.FileName << F.ModuleName;
3375+
return ASTReadResult::ModuleMismatch;
3376+
}
3377+
33663378
// Validate the AST as soon as we have a name so we can exit early on
33673379
// failure.
33683380
if (ASTReadResult Result = readUnhashedControlBlockOnce())
@@ -4684,6 +4696,15 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, ModuleKind Type,
46844696
SourceLocation ImportLoc,
46854697
unsigned ClientLoadCapabilities,
46864698
ModuleFile **NewLoadedModuleFile) {
4699+
return ReadAST(FileName, Type, ImportLoc, ClientLoadCapabilities, "",
4700+
NewLoadedModuleFile);
4701+
}
4702+
4703+
ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, ModuleKind Type,
4704+
SourceLocation ImportLoc,
4705+
unsigned ClientLoadCapabilities,
4706+
StringRef ExpectedModuleName,
4707+
ModuleFile **NewLoadedModuleFile) {
46874708
llvm::TimeTraceScope scope("ReadAST", FileName);
46884709

46894710
llvm::SaveAndRestore SetCurImportLocRAII(CurrentImportLoc, ImportLoc);
@@ -4702,8 +4723,8 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, ModuleKind Type,
47024723
SmallVector<ImportedModule, 4> Loaded;
47034724
if (ASTReadResult ReadResult =
47044725
ReadASTCore(FileName, Type, ImportLoc,
4705-
/*ImportedBy=*/nullptr, Loaded, 0, 0, ASTFileSignature(),
4706-
ClientLoadCapabilities)) {
4726+
/*ImportedBy=*/nullptr, Loaded, 0, 0, ExpectedModuleName,
4727+
ASTFileSignature(), ClientLoadCapabilities)) {
47074728
ModuleMgr.removeModules(ModuleMgr.begin() + NumModules);
47084729

47094730
// If we find that any modules are unusable, the global index is going
@@ -4954,25 +4975,26 @@ static unsigned moduleKindForDiagnostic(ModuleKind Kind) {
49544975
llvm_unreachable("unknown module kind");
49554976
}
49564977

4957-
ASTReader::ASTReadResult
4958-
ASTReader::ReadASTCore(StringRef FileName,
4959-
ModuleKind Type,
4960-
SourceLocation ImportLoc,
4961-
ModuleFile *ImportedBy,
4962-
SmallVectorImpl<ImportedModule> &Loaded,
4963-
off_t ExpectedSize, time_t ExpectedModTime,
4964-
ASTFileSignature ExpectedSignature,
4965-
unsigned ClientLoadCapabilities) {
4978+
ASTReader::ASTReadResult ASTReader::ReadASTCore(
4979+
StringRef FileName, ModuleKind Type, SourceLocation ImportLoc,
4980+
ModuleFile *ImportedBy, SmallVectorImpl<ImportedModule> &Loaded,
4981+
off_t ExpectedSize, time_t ExpectedModTime, StringRef ExpectedModuleName,
4982+
ASTFileSignature ExpectedSignature, unsigned ClientLoadCapabilities) {
49664983
ModuleFile *M;
49674984
std::string ErrorStr;
4968-
ModuleManager::AddModuleResult AddResult
4969-
= ModuleMgr.addModule(FileName, Type, ImportLoc, ImportedBy,
4970-
getGeneration(), ExpectedSize, ExpectedModTime,
4971-
ExpectedSignature, readASTFileSignature,
4972-
M, ErrorStr);
4985+
ModuleManager::AddModuleResult AddResult = ModuleMgr.addModule(
4986+
FileName, Type, ImportLoc, ImportedBy, getGeneration(), ExpectedSize,
4987+
ExpectedModTime, ExpectedSignature, readASTFileSignature, M, ErrorStr);
49734988

49744989
switch (AddResult) {
49754990
case ModuleManager::AlreadyLoaded:
4991+
// Return if the AST unexpectedly contains a different module
4992+
if (Type == MK_PrebuiltModule && !ExpectedModuleName.empty() &&
4993+
M->ModuleName != ExpectedModuleName) {
4994+
Diag(diag::err_module_mismatch)
4995+
<< ExpectedModuleName << FileName << M->ModuleName;
4996+
return ASTReadResult::ModuleMismatch;
4997+
}
49764998
Diag(diag::remark_module_import)
49774999
<< M->ModuleName << M->FileName << (ImportedBy ? true : false)
49785000
<< (ImportedBy ? StringRef(ImportedBy->ModuleName) : StringRef());
@@ -5053,7 +5075,8 @@ ASTReader::ReadASTCore(StringRef FileName,
50535075
switch (Entry.ID) {
50545076
case CONTROL_BLOCK_ID:
50555077
HaveReadControlBlock = true;
5056-
switch (ReadControlBlock(F, Loaded, ImportedBy, ClientLoadCapabilities)) {
5078+
switch (ReadControlBlock(F, Loaded, ImportedBy, ExpectedModuleName,
5079+
ClientLoadCapabilities)) {
50575080
case Success:
50585081
// Check that we didn't try to load a non-module AST file as a module.
50595082
//
@@ -5075,6 +5098,8 @@ ASTReader::ReadASTCore(StringRef FileName,
50755098
case OutOfDate: return OutOfDate;
50765099
case VersionMismatch: return VersionMismatch;
50775100
case ConfigurationMismatch: return ConfigurationMismatch;
5101+
case ModuleMismatch:
5102+
return ModuleMismatch;
50785103
case HadErrors: return HadErrors;
50795104
}
50805105
break;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
// RUN: cd %t
4+
5+
// Related to issue #132059
6+
7+
// Precompile the module dependencies correctly
8+
// RUN: %clang_cc1 -std=c++20 -emit-module-interface a.cppm -o a.pcm
9+
// RUN: %clang_cc1 -std=c++20 -emit-module-interface b.cppm -o b.pcm -fmodule-file=A=a.pcm
10+
11+
// Test that providing incorrect mappings via -fmodule-file=<name>=<path/to/bmi>,
12+
// where the BMI is built for a different module than the one specified and
13+
// transitively imports the specified module, does not crash the compiler.
14+
// RUN: not %clang_cc1 -std=c++20 main1.cpp -fmodule-file=A=b.pcm
15+
16+
//--- a.cppm
17+
export module A;
18+
19+
export int a() {
20+
return 41;
21+
}
22+
23+
//--- b.cppm
24+
export module B;
25+
import A;
26+
27+
export int b() {
28+
return a() + 1;
29+
}
30+
31+
//--- main1.cpp
32+
import A;
33+
34+
int main() {
35+
return a();
36+
}
37+
38+
// Test again for the case where the BMI is first loaded correctly
39+
// RUN: not %clang_cc1 -std=c++20 main2.cpp-fmodule-file=B=b.pcm -fmodule-file=A=b.pcm
40+
41+
//--- main2.cpp
42+
import B;
43+
44+
int main() {
45+
return b();
46+
}

0 commit comments

Comments
 (0)