mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Serialization] Restrict loading swiftmodule files to the builder's SDK
Serialize the canonical name of the SDK used when building a swiftmodule file and use it to ensure that the swiftmodule file is loaded only with the same SDK. The SDK name must be passed down from the frontend. This will report unsupported configurations like: - Installing roots between incompatible SDKs without deleting the swiftmodule files. - Having multiple targets in the same project using different SDKs. - Loading a swiftmodule created with a newer SDK (and stdlib) with an older SDK. All of these lead to hard to investigate deserialization failures and this change should detect them early, before reaching a deserialization failure. rdar://78048939
This commit is contained in:
@@ -782,6 +782,9 @@ ERROR(serialization_name_mismatch_repl,none,
|
||||
ERROR(serialization_target_incompatible,Fatal,
|
||||
"module %0 was created for incompatible target %1: %2",
|
||||
(Identifier, StringRef, StringRef))
|
||||
ERROR(serialization_sdk_mismatch,Fatal,
|
||||
"cannot load module %0 built with SDK '%1' when using SDK '%2': %3",
|
||||
(Identifier, StringRef, StringRef, StringRef))
|
||||
ERROR(serialization_target_incompatible_repl,none,
|
||||
"module %0 was created for incompatible target %1: %2",
|
||||
(Identifier, StringRef, StringRef))
|
||||
|
||||
@@ -82,6 +82,10 @@ public:
|
||||
/// would for a non-system header.
|
||||
bool DisableModulesValidateSystemDependencies = false;
|
||||
|
||||
/// Enforce loading only serialized modules built with the same SDK
|
||||
/// as the context loading it.
|
||||
bool EnableSameSDKCheck = true;
|
||||
|
||||
/// A set of compiled modules that may be ready to use.
|
||||
std::vector<std::string> CandidateCompiledModules;
|
||||
|
||||
|
||||
@@ -127,6 +127,9 @@ namespace swift {
|
||||
/// The target variant SDK version, if known.
|
||||
Optional<llvm::VersionTuple> VariantSDKVersion;
|
||||
|
||||
/// The SDK canonical name, if known.
|
||||
std::string SDKName;
|
||||
|
||||
/// The alternate name to use for the entry point instead of main.
|
||||
std::string entryPointFunctionName = "main";
|
||||
|
||||
|
||||
@@ -823,6 +823,9 @@ def target_sdk_version : Separate<["-"], "target-sdk-version">,
|
||||
def target_variant_sdk_version : Separate<["-"], "target-variant-sdk-version">,
|
||||
HelpText<"The version of target variant SDK used for compilation">;
|
||||
|
||||
def target_sdk_name : Separate<["-"], "target-sdk-name">,
|
||||
HelpText<"Canonical name of the target SDK used for compilation">;
|
||||
|
||||
def extra_clang_options_only : Flag<["-"], "only-use-extra-clang-opts">,
|
||||
HelpText<"Options passed via -Xcc are sufficient for Clang configuration">;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace swift {
|
||||
bool SkipSymbolGraphInheritedDocs = true;
|
||||
bool IncludeSPISymbolsInSymbolGraph = false;
|
||||
llvm::VersionTuple UserModuleVersion;
|
||||
std::string SDKName;
|
||||
|
||||
StringRef GroupInfoPath;
|
||||
StringRef ImportedHeader;
|
||||
|
||||
@@ -66,7 +66,11 @@ enum class Status {
|
||||
TargetIncompatible,
|
||||
|
||||
/// The module file was built for a target newer than the current target.
|
||||
TargetTooNew
|
||||
TargetTooNew,
|
||||
|
||||
/// The module file was built with a different SDK than the one in use
|
||||
/// to build the client.
|
||||
SDKMismatch
|
||||
};
|
||||
|
||||
/// Returns true if the data looks like it contains a serialized AST.
|
||||
@@ -80,6 +84,7 @@ struct ValidationInfo {
|
||||
StringRef miscVersion = {};
|
||||
version::Version compatibilityVersion = {};
|
||||
llvm::VersionTuple userModuleVersion;
|
||||
StringRef sdkName = {};
|
||||
size_t bytes = 0;
|
||||
Status status = Status::Malformed;
|
||||
};
|
||||
|
||||
@@ -774,6 +774,11 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
|
||||
}
|
||||
}
|
||||
|
||||
// Get the SDK name.
|
||||
if (Arg *A = Args.getLastArg(options::OPT_target_sdk_name)) {
|
||||
Opts.SDKName = A->getValue();
|
||||
}
|
||||
|
||||
if (const Arg *A = Args.getLastArg(OPT_entry_point_function_name)) {
|
||||
Opts.entryPointFunctionName = A->getValue();
|
||||
}
|
||||
|
||||
@@ -150,6 +150,7 @@ SerializationOptions CompilerInvocation::computeSerializationOptions(
|
||||
serializationOpts.ExtraClangOptions = getClangImporterOptions().ExtraArgs;
|
||||
serializationOpts.PublicDependentLibraries =
|
||||
getIRGenOptions().PublicLinkLibraries;
|
||||
serializationOpts.SDKName = getLangOptions().SDKName;
|
||||
|
||||
if (opts.EmitSymbolGraph) {
|
||||
if (!opts.SymbolGraphOutputDir.empty()) {
|
||||
|
||||
@@ -257,6 +257,8 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal(
|
||||
if (!getRelativeDepPath(InPath, SDKPath))
|
||||
SerializationOpts.ModuleInterface = InPath;
|
||||
|
||||
SerializationOpts.SDKName = SubInstance.getASTContext().LangOpts.SDKName;
|
||||
|
||||
SmallVector<FileDependency, 16> Deps;
|
||||
bool serializeHashes = FEOpts.SerializeModuleInterfaceDependencyHashes;
|
||||
if (collectDepsForSerialization(SubInstance, Deps, serializeHashes)) {
|
||||
|
||||
@@ -149,6 +149,15 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
|
||||
return error(status);
|
||||
}
|
||||
|
||||
auto clientSDK = ctx.LangOpts.SDKName;
|
||||
StringRef moduleSDK = Core->SDKName;
|
||||
if (ctx.SearchPathOpts.EnableSameSDKCheck &&
|
||||
!moduleSDK.empty() && !clientSDK.empty() &&
|
||||
moduleSDK != clientSDK) {
|
||||
status = Status::SDKMismatch;
|
||||
return error(status);
|
||||
}
|
||||
|
||||
for (const auto &searchPath : Core->SearchPaths)
|
||||
ctx.addSearchPath(searchPath.Path, searchPath.IsFramework,
|
||||
searchPath.IsSystem);
|
||||
|
||||
@@ -299,6 +299,10 @@ validateControlBlock(llvm::BitstreamCursor &cursor,
|
||||
case control_block::TARGET:
|
||||
result.targetTriple = blobData;
|
||||
break;
|
||||
case control_block::SDK_NAME: {
|
||||
result.sdkName = blobData;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Unknown metadata record, possibly for use by a future version of the
|
||||
// module format.
|
||||
@@ -1210,6 +1214,7 @@ ModuleFileSharedCore::ModuleFileSharedCore(
|
||||
}
|
||||
Name = info.name;
|
||||
TargetTriple = info.targetTriple;
|
||||
SDKName = info.sdkName;
|
||||
CompatibilityVersion = info.compatibilityVersion;
|
||||
UserModuleVersion = info.userModuleVersion;
|
||||
Bits.ArePrivateImportsEnabled = extInfo.arePrivateImportsEnabled();
|
||||
|
||||
@@ -58,6 +58,9 @@ class ModuleFileSharedCore {
|
||||
/// The target the module was built for.
|
||||
StringRef TargetTriple;
|
||||
|
||||
/// The canonical name of the SDK the module was built with.
|
||||
StringRef SDKName;
|
||||
|
||||
/// The name of the module interface this module was compiled from.
|
||||
///
|
||||
/// Empty if this module didn't come from an interface file.
|
||||
|
||||
@@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
|
||||
/// describe what change you made. The content of this comment isn't important;
|
||||
/// it just ensures a conflict if two people change the module format.
|
||||
/// Don't worry about adhering to the 80-column limit for this line.
|
||||
const uint16_t SWIFTMODULE_VERSION_MINOR = 628; // unaligned pointer
|
||||
const uint16_t SWIFTMODULE_VERSION_MINOR = 629; // BuilderSDK
|
||||
|
||||
/// A standard hash seed used for all string hashes in a serialized module.
|
||||
///
|
||||
@@ -754,7 +754,8 @@ namespace control_block {
|
||||
enum {
|
||||
METADATA = 1,
|
||||
MODULE_NAME,
|
||||
TARGET
|
||||
TARGET,
|
||||
SDK_NAME
|
||||
};
|
||||
|
||||
using MetadataLayout = BCRecordLayout<
|
||||
@@ -779,6 +780,11 @@ namespace control_block {
|
||||
TARGET,
|
||||
BCBlob // LLVM triple
|
||||
>;
|
||||
|
||||
using SDKNameLayout = BCRecordLayout<
|
||||
SDK_NAME,
|
||||
BCBlob
|
||||
>;
|
||||
}
|
||||
|
||||
/// The record types within the options block (a sub-block of the control
|
||||
|
||||
@@ -802,6 +802,7 @@ void Serializer::writeBlockInfoBlock() {
|
||||
BLOCK_RECORD(control_block, METADATA);
|
||||
BLOCK_RECORD(control_block, MODULE_NAME);
|
||||
BLOCK_RECORD(control_block, TARGET);
|
||||
BLOCK_RECORD(control_block, SDK_NAME);
|
||||
|
||||
BLOCK(OPTIONS_BLOCK);
|
||||
BLOCK_RECORD(options_block, SDK_PATH);
|
||||
@@ -952,6 +953,7 @@ void Serializer::writeHeader(const SerializationOptions &options) {
|
||||
control_block::ModuleNameLayout ModuleName(Out);
|
||||
control_block::MetadataLayout Metadata(Out);
|
||||
control_block::TargetLayout Target(Out);
|
||||
control_block::SDKNameLayout SDKName(Out);
|
||||
|
||||
ModuleName.emit(ScratchRecord, M->getName().str());
|
||||
|
||||
@@ -985,6 +987,9 @@ void Serializer::writeHeader(const SerializationOptions &options) {
|
||||
userModuleSubminor, userModuleBuild,
|
||||
versionString.str());
|
||||
|
||||
if (!options.SDKName.empty())
|
||||
SDKName.emit(ScratchRecord, options.SDKName);
|
||||
|
||||
Target.emit(ScratchRecord, M->getASTContext().LangOpts.Target.str());
|
||||
|
||||
{
|
||||
|
||||
@@ -976,6 +976,13 @@ void swift::serialization::diagnoseSerializedASTLoadFailure(
|
||||
moduleOSInfo.second, moduleBufferID);
|
||||
break;
|
||||
}
|
||||
|
||||
case serialization::Status::SDKMismatch:
|
||||
auto currentSDK = Ctx.LangOpts.SDKName;
|
||||
auto moduleSDK = loadInfo.sdkName;
|
||||
Ctx.Diags.diagnose(diagLoc, diag::serialization_sdk_mismatch,
|
||||
ModuleName, moduleSDK, currentSDK, moduleBufferID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
20
test/Serialization/restrict-swiftmodule-to-sdk.swift
Normal file
20
test/Serialization/restrict-swiftmodule-to-sdk.swift
Normal file
@@ -0,0 +1,20 @@
|
||||
// RUN: %empty-directory(%t/cache)
|
||||
// RUN: %empty-directory(%t/build)
|
||||
// RUN: %{python} %utils/split_file.py -o %t %s
|
||||
|
||||
/// Build Lib against SDK A.
|
||||
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 -target-sdk-name A -o %t/build -parse-stdlib -module-cache-path %t/cache
|
||||
|
||||
/// Building Client against SDK A should work fine as expected.
|
||||
// RUN: %target-swift-frontend -typecheck %t/Client.swift -swift-version 5 -target-sdk-name A -I %t/build -parse-stdlib -module-cache-path %t/cache
|
||||
|
||||
/// Build Client against SDK B, this should fail at loading Lib against a different SDK than A.
|
||||
// RUN: not %target-swift-frontend -typecheck %t/Client.swift -swift-version 5 -target-sdk-name B -I %t/build -parse-stdlib -module-cache-path %t/cache 2>&1 | %FileCheck %s
|
||||
// CHECK: cannot load module 'Lib' built with SDK 'A' when using SDK 'B': {{.*}}Lib.swiftmodule
|
||||
|
||||
// BEGIN Lib.swift
|
||||
public func foo() {}
|
||||
|
||||
// BEGIN Client.swift
|
||||
import Lib
|
||||
foo()
|
||||
Reference in New Issue
Block a user