Skip to content

[Serialization] Restrict loading swiftmodule files to the SDK that built them #37768

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/SearchPathOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ class SearchPathOptions {
/// 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;

Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down
3 changes: 3 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;

Expand Down
1 change: 1 addition & 0 deletions include/swift/Serialization/SerializationOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ namespace swift {
bool SkipSymbolGraphInheritedDocs = true;
bool IncludeSPISymbolsInSymbolGraph = false;
llvm::VersionTuple UserModuleVersion;
std::string SDKName;

StringRef GroupInfoPath;
StringRef ImportedHeader;
Expand Down
7 changes: 6 additions & 1 deletion include/swift/Serialization/Validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -80,6 +84,7 @@ struct ValidationInfo {
StringRef miscVersion = {};
version::Version compatibilityVersion = {};
llvm::VersionTuple userModuleVersion;
StringRef sdkName = {};
size_t bytes = 0;
Status status = Status::Malformed;
};
Expand Down
3 changes: 3 additions & 0 deletions lib/APIDigester/ModuleAnalyzerNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2233,6 +2233,9 @@ swift::ide::api::getSDKNodeRoot(SDKContext &SDKCtx,

auto &Ctx = CI.getASTContext();

// Don't check if the stdlib was build with the same SDK as what is loaded
// here as some tests rely on using a different stdlib.
Ctx.SearchPathOpts.EnableSameSDKCheck = false;

// Load standard library so that Clang importer can use it.
auto *Stdlib = Ctx.getStdlibModule(/*loadIfAbsent=*/true);
Expand Down
5 changes: 5 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
1 change: 1 addition & 0 deletions lib/Frontend/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
2 changes: 2 additions & 0 deletions lib/Frontend/ModuleInterfaceBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
9 changes: 9 additions & 0 deletions lib/Serialization/ModuleFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions lib/Serialization/ModuleFileSharedCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions lib/Serialization/ModuleFileSharedCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 8 additions & 2 deletions lib/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -754,7 +754,8 @@ namespace control_block {
enum {
METADATA = 1,
MODULE_NAME,
TARGET
TARGET,
SDK_NAME
};

using MetadataLayout = BCRecordLayout<
Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions lib/Serialization/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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());

Expand Down Expand Up @@ -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());

{
Expand Down
7 changes: 7 additions & 0 deletions lib/Serialization/SerializedModuleLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/DebugInfo/compileunit-sysroot-sdk.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend %s -emit-ir -g -o - \
// RUN: %target-swift-frontend %s -emit-ir -g -o - -parse-stdlib \
// RUN: -sdk /SWIFT_SYSROOT/MacOSX.sdk | %FileCheck %s
// Test that sysroot and SDK are stored in the debug info.
// CHECK: distinct !DICompileUnit({{.*}}sysroot: "/SWIFT_SYSROOT/MacOSX.sdk",
Expand Down
20 changes: 20 additions & 0 deletions test/Serialization/restrict-swiftmodule-to-sdk.swift
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// swift-interface-format-version: 1.0
// swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14)
// swift-module-flags: -target arm64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo
import Swift
// swift-module-flags: -target arm64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo -parse-stdlib
public class RemovedClass {
@objc deinit
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// swift-interface-format-version: 1.0
// swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14)
// swift-module-flags: -target x86_64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo
import Swift
// swift-module-flags: -target x86_64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo -parse-stdlib
public class RemovedClass {
@objc deinit
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// swift-interface-format-version: 1.0
// swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14)
// swift-module-flags: -target arm64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo
import Swift
// swift-module-flags: -target arm64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo -parse-stdlib
public class AddedClass {
@objc deinit
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// swift-interface-format-version: 1.0
// swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14)
// swift-module-flags: -target x86_64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo
import Swift
// swift-module-flags: -target x86_64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo -parse-stdlib
public class AddedClass {
@objc deinit
}