From c38d1773d23fccb6fb3bc5d885d7aee2380cf4eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Tue, 1 Jun 2021 16:57:40 -0700 Subject: [PATCH] [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 --- include/swift/AST/DiagnosticsSema.def | 3 +++ include/swift/AST/SearchPathOptions.h | 4 ++++ include/swift/Basic/LangOptions.h | 3 +++ include/swift/Option/FrontendOptions.td | 3 +++ .../Serialization/SerializationOptions.h | 1 + include/swift/Serialization/Validation.h | 7 ++++++- lib/Frontend/CompilerInvocation.cpp | 5 +++++ lib/Frontend/Frontend.cpp | 1 + lib/Frontend/ModuleInterfaceBuilder.cpp | 2 ++ lib/Serialization/ModuleFile.cpp | 9 +++++++++ lib/Serialization/ModuleFileSharedCore.cpp | 5 +++++ lib/Serialization/ModuleFileSharedCore.h | 3 +++ lib/Serialization/ModuleFormat.h | 10 ++++++++-- lib/Serialization/Serialization.cpp | 5 +++++ lib/Serialization/SerializedModuleLoader.cpp | 7 +++++++ .../restrict-swiftmodule-to-sdk.swift | 20 +++++++++++++++++++ 16 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 test/Serialization/restrict-swiftmodule-to-sdk.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 5227dfa4e7c..b3b32591277 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -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)) diff --git a/include/swift/AST/SearchPathOptions.h b/include/swift/AST/SearchPathOptions.h index f873e82ada3..02f61eadfe5 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -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 CandidateCompiledModules; diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index f8d161d346e..6d14419be80 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -127,6 +127,9 @@ namespace swift { /// The target variant SDK version, if known. Optional 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"; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 0097ea215f0..7ea2ec76be3 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -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">; diff --git a/include/swift/Serialization/SerializationOptions.h b/include/swift/Serialization/SerializationOptions.h index 2b180591a19..8f19f7d9ed2 100644 --- a/include/swift/Serialization/SerializationOptions.h +++ b/include/swift/Serialization/SerializationOptions.h @@ -35,6 +35,7 @@ namespace swift { bool SkipSymbolGraphInheritedDocs = true; bool IncludeSPISymbolsInSymbolGraph = false; llvm::VersionTuple UserModuleVersion; + std::string SDKName; StringRef GroupInfoPath; StringRef ImportedHeader; diff --git a/include/swift/Serialization/Validation.h b/include/swift/Serialization/Validation.h index e5564a13947..a2ffe970ed9 100644 --- a/include/swift/Serialization/Validation.h +++ b/include/swift/Serialization/Validation.h @@ -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; }; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index a854fc85e2d..4c740f3cb4a 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -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(); } diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index de292051246..685974fb554 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -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()) { diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index 4c8a3e18579..19a3240826c 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -257,6 +257,8 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( if (!getRelativeDepPath(InPath, SDKPath)) SerializationOpts.ModuleInterface = InPath; + SerializationOpts.SDKName = SubInstance.getASTContext().LangOpts.SDKName; + SmallVector Deps; bool serializeHashes = FEOpts.SerializeModuleInterfaceDependencyHashes; if (collectDepsForSerialization(SubInstance, Deps, serializeHashes)) { diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index 1d2aeeb0276..38990f4b4e4 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -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); diff --git a/lib/Serialization/ModuleFileSharedCore.cpp b/lib/Serialization/ModuleFileSharedCore.cpp index 8fba7e2ed85..eeec9a2fef7 100644 --- a/lib/Serialization/ModuleFileSharedCore.cpp +++ b/lib/Serialization/ModuleFileSharedCore.cpp @@ -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(); diff --git a/lib/Serialization/ModuleFileSharedCore.h b/lib/Serialization/ModuleFileSharedCore.h index e192013c596..7f8fde8b284 100644 --- a/lib/Serialization/ModuleFileSharedCore.h +++ b/lib/Serialization/ModuleFileSharedCore.h @@ -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. diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 599ef3e5c05..18e9530e861 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -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 diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 92ce769c885..0720a001b72 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -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()); { diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 57c34431437..5d5dac786cd 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -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; } } diff --git a/test/Serialization/restrict-swiftmodule-to-sdk.swift b/test/Serialization/restrict-swiftmodule-to-sdk.swift new file mode 100644 index 00000000000..5c678fe70a3 --- /dev/null +++ b/test/Serialization/restrict-swiftmodule-to-sdk.swift @@ -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()