mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Serialization: restrict swiftmodules to distribution channels
There are scenarios where different compilers are distributed with compatible serialization format versions and the same tag. Distinguish swiftmodules in such a case by assigning them to different distribution channels. A compiler expecting a specific channel will only read swiftmodules from the same channel. The channels should be defined by downstream code as it is by definition vendor specific. For development, a no-channel compiler loads or defining the env var SWIFT_IGNORE_SWIFTMODULE_REVISION skips this new check. rdar://123731777
This commit is contained in:
@@ -850,6 +850,11 @@ ERROR(serialization_module_incompatible_revision,Fatal,
|
|||||||
"compiled module was created by a different version of the compiler '%0'; "
|
"compiled module was created by a different version of the compiler '%0'; "
|
||||||
"rebuild %1 and try again: %2",
|
"rebuild %1 and try again: %2",
|
||||||
(StringRef, Identifier, StringRef))
|
(StringRef, Identifier, StringRef))
|
||||||
|
ERROR(serialization_module_incompatible_channel,Fatal,
|
||||||
|
"compiled module was created for a different distribution channel '%0' "
|
||||||
|
"than the local compiler '%1', "
|
||||||
|
"please ensure %2 is found from the expected path: %3",
|
||||||
|
(StringRef, StringRef, Identifier, StringRef))
|
||||||
ERROR(serialization_missing_single_dependency,Fatal,
|
ERROR(serialization_missing_single_dependency,Fatal,
|
||||||
"missing required module '%0'", (StringRef))
|
"missing required module '%0'", (StringRef))
|
||||||
ERROR(serialization_missing_dependencies,Fatal,
|
ERROR(serialization_missing_dependencies,Fatal,
|
||||||
|
|||||||
@@ -177,6 +177,11 @@ StringRef getCurrentCompilerTag();
|
|||||||
/// depending on the vendor.
|
/// depending on the vendor.
|
||||||
StringRef getCurrentCompilerSerializationTag();
|
StringRef getCurrentCompilerSerializationTag();
|
||||||
|
|
||||||
|
/// Distribution channel of the running compiler for distributed swiftmodules.
|
||||||
|
/// Helps to distinguish swiftmodules between different compilers using the
|
||||||
|
/// same serialization tag.
|
||||||
|
StringRef getCurrentCompilerChannel();
|
||||||
|
|
||||||
/// Retrieves the value of the upcoming C++ interoperability compatibility
|
/// Retrieves the value of the upcoming C++ interoperability compatibility
|
||||||
/// version that's going to be presented as some new concrete version to the
|
/// version that's going to be presented as some new concrete version to the
|
||||||
/// users.
|
/// users.
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ enum class Status {
|
|||||||
/// The precise revision version doesn't match.
|
/// The precise revision version doesn't match.
|
||||||
RevisionIncompatible,
|
RevisionIncompatible,
|
||||||
|
|
||||||
|
/// The distribution channel doesn't match.
|
||||||
|
ChannelIncompatible,
|
||||||
|
|
||||||
/// The module is required to be in OSSA, but is not.
|
/// The module is required to be in OSSA, but is not.
|
||||||
NotInOSSA,
|
NotInOSSA,
|
||||||
|
|
||||||
@@ -105,6 +108,7 @@ struct ValidationInfo {
|
|||||||
llvm::VersionTuple userModuleVersion;
|
llvm::VersionTuple userModuleVersion;
|
||||||
StringRef sdkName = {};
|
StringRef sdkName = {};
|
||||||
StringRef problematicRevision = {};
|
StringRef problematicRevision = {};
|
||||||
|
StringRef problematicChannel = {};
|
||||||
size_t bytes = 0;
|
size_t bytes = 0;
|
||||||
Status status = Status::Malformed;
|
Status status = Status::Malformed;
|
||||||
std::vector<StringRef> allowableClients;
|
std::vector<StringRef> allowableClients;
|
||||||
|
|||||||
@@ -313,6 +313,16 @@ StringRef getCurrentCompilerSerializationTag() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringRef getCurrentCompilerChannel() {
|
||||||
|
static const char* forceDebugChannel =
|
||||||
|
::getenv("SWIFT_FORCE_SWIFTMODULE_CHANNEL");
|
||||||
|
if (forceDebugChannel)
|
||||||
|
return forceDebugChannel;
|
||||||
|
|
||||||
|
// Leave it to downstream compilers to define the different channels.
|
||||||
|
return StringRef();
|
||||||
|
}
|
||||||
|
|
||||||
unsigned getUpcomingCxxInteropCompatVersion() {
|
unsigned getUpcomingCxxInteropCompatVersion() {
|
||||||
return SWIFT_VERSION_MAJOR + 1;
|
return SWIFT_VERSION_MAJOR + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,6 +316,8 @@ struct ModuleRebuildInfo {
|
|||||||
return "compiled with a newer version of the compiler";
|
return "compiled with a newer version of the compiler";
|
||||||
case Status::RevisionIncompatible:
|
case Status::RevisionIncompatible:
|
||||||
return "compiled with a different version of the compiler";
|
return "compiled with a different version of the compiler";
|
||||||
|
case Status::ChannelIncompatible:
|
||||||
|
return "compiled for a different distribution channel";
|
||||||
case Status::NotInOSSA:
|
case Status::NotInOSSA:
|
||||||
return "module was not built with OSSA";
|
return "module was not built with OSSA";
|
||||||
case Status::NoncopyableGenericsMismatch:
|
case Status::NoncopyableGenericsMismatch:
|
||||||
|
|||||||
@@ -418,6 +418,21 @@ static ValidationInfo validateControlBlock(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case control_block::CHANNEL: {
|
||||||
|
static const char* ignoreRevision =
|
||||||
|
::getenv("SWIFT_IGNORE_SWIFTMODULE_REVISION");
|
||||||
|
if (ignoreRevision)
|
||||||
|
break;
|
||||||
|
|
||||||
|
StringRef moduleChannel = blobData,
|
||||||
|
compilerChannel = version::getCurrentCompilerChannel();
|
||||||
|
if (requiresRevisionMatch && !compilerChannel.empty() &&
|
||||||
|
moduleChannel != compilerChannel) {
|
||||||
|
result.problematicChannel = moduleChannel;
|
||||||
|
result.status = Status::ChannelIncompatible;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case control_block::IS_OSSA: {
|
case control_block::IS_OSSA: {
|
||||||
auto isModuleInOSSA = scratch[0];
|
auto isModuleInOSSA = scratch[0];
|
||||||
if (requiresOSSAModules && !isModuleInOSSA)
|
if (requiresOSSAModules && !isModuleInOSSA)
|
||||||
@@ -532,6 +547,7 @@ std::string serialization::StatusToString(Status S) {
|
|||||||
case Status::FormatTooOld: return "FormatTooOld";
|
case Status::FormatTooOld: return "FormatTooOld";
|
||||||
case Status::FormatTooNew: return "FormatTooNew";
|
case Status::FormatTooNew: return "FormatTooNew";
|
||||||
case Status::RevisionIncompatible: return "RevisionIncompatible";
|
case Status::RevisionIncompatible: return "RevisionIncompatible";
|
||||||
|
case Status::ChannelIncompatible: return "ChannelIncompatible";
|
||||||
case Status::NotInOSSA: return "NotInOSSA";
|
case Status::NotInOSSA: return "NotInOSSA";
|
||||||
case Status::NoncopyableGenericsMismatch:
|
case Status::NoncopyableGenericsMismatch:
|
||||||
return "NoncopyableGenericsMismatch";
|
return "NoncopyableGenericsMismatch";
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
|
|||||||
/// describe what change you made. The content of this comment isn't important;
|
/// 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.
|
/// 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.
|
/// Don't worry about adhering to the 80-column limit for this line.
|
||||||
const uint16_t SWIFTMODULE_VERSION_MINOR = 858; // Replace inherited types with protocols in protocol layout
|
const uint16_t SWIFTMODULE_VERSION_MINOR = 859; // channel check
|
||||||
|
|
||||||
/// A standard hash seed used for all string hashes in a serialized module.
|
/// A standard hash seed used for all string hashes in a serialized module.
|
||||||
///
|
///
|
||||||
@@ -860,6 +860,7 @@ namespace control_block {
|
|||||||
TARGET,
|
TARGET,
|
||||||
SDK_NAME,
|
SDK_NAME,
|
||||||
REVISION,
|
REVISION,
|
||||||
|
CHANNEL,
|
||||||
IS_OSSA,
|
IS_OSSA,
|
||||||
ALLOWABLE_CLIENT_NAME,
|
ALLOWABLE_CLIENT_NAME,
|
||||||
HAS_NONCOPYABLE_GENERICS,
|
HAS_NONCOPYABLE_GENERICS,
|
||||||
@@ -898,6 +899,11 @@ namespace control_block {
|
|||||||
BCBlob
|
BCBlob
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
using ChannelLayout = BCRecordLayout<
|
||||||
|
CHANNEL,
|
||||||
|
BCBlob
|
||||||
|
>;
|
||||||
|
|
||||||
using IsOSSALayout = BCRecordLayout<
|
using IsOSSALayout = BCRecordLayout<
|
||||||
IS_OSSA,
|
IS_OSSA,
|
||||||
BCFixed<1>
|
BCFixed<1>
|
||||||
|
|||||||
@@ -834,6 +834,7 @@ void Serializer::writeBlockInfoBlock() {
|
|||||||
BLOCK_RECORD(control_block, TARGET);
|
BLOCK_RECORD(control_block, TARGET);
|
||||||
BLOCK_RECORD(control_block, SDK_NAME);
|
BLOCK_RECORD(control_block, SDK_NAME);
|
||||||
BLOCK_RECORD(control_block, REVISION);
|
BLOCK_RECORD(control_block, REVISION);
|
||||||
|
BLOCK_RECORD(control_block, CHANNEL);
|
||||||
BLOCK_RECORD(control_block, IS_OSSA);
|
BLOCK_RECORD(control_block, IS_OSSA);
|
||||||
BLOCK_RECORD(control_block, ALLOWABLE_CLIENT_NAME);
|
BLOCK_RECORD(control_block, ALLOWABLE_CLIENT_NAME);
|
||||||
BLOCK_RECORD(control_block, HAS_NONCOPYABLE_GENERICS);
|
BLOCK_RECORD(control_block, HAS_NONCOPYABLE_GENERICS);
|
||||||
@@ -982,6 +983,7 @@ void Serializer::writeHeader() {
|
|||||||
control_block::TargetLayout Target(Out);
|
control_block::TargetLayout Target(Out);
|
||||||
control_block::SDKNameLayout SDKName(Out);
|
control_block::SDKNameLayout SDKName(Out);
|
||||||
control_block::RevisionLayout Revision(Out);
|
control_block::RevisionLayout Revision(Out);
|
||||||
|
control_block::ChannelLayout Channel(Out);
|
||||||
control_block::IsOSSALayout IsOSSA(Out);
|
control_block::IsOSSALayout IsOSSA(Out);
|
||||||
control_block::AllowableClientLayout Allowable(Out);
|
control_block::AllowableClientLayout Allowable(Out);
|
||||||
control_block::HasNoncopyableGenerics HasNoncopyableGenerics(Out);
|
control_block::HasNoncopyableGenerics HasNoncopyableGenerics(Out);
|
||||||
@@ -1035,6 +1037,8 @@ void Serializer::writeHeader() {
|
|||||||
forcedDebugRevision : version::getCurrentCompilerSerializationTag();
|
forcedDebugRevision : version::getCurrentCompilerSerializationTag();
|
||||||
Revision.emit(ScratchRecord, revision);
|
Revision.emit(ScratchRecord, revision);
|
||||||
|
|
||||||
|
Channel.emit(ScratchRecord, version::getCurrentCompilerChannel());
|
||||||
|
|
||||||
IsOSSA.emit(ScratchRecord, Options.IsOSSA);
|
IsOSSA.emit(ScratchRecord, Options.IsOSSA);
|
||||||
|
|
||||||
HasNoncopyableGenerics.emit(ScratchRecord,
|
HasNoncopyableGenerics.emit(ScratchRecord,
|
||||||
|
|||||||
@@ -1081,6 +1081,12 @@ void swift::serialization::diagnoseSerializedASTLoadFailure(
|
|||||||
Ctx.Diags.diagnose(diagLoc, diag::serialization_module_incompatible_revision,
|
Ctx.Diags.diagnose(diagLoc, diag::serialization_module_incompatible_revision,
|
||||||
loadInfo.problematicRevision, ModuleName, moduleBufferID);
|
loadInfo.problematicRevision, ModuleName, moduleBufferID);
|
||||||
break;
|
break;
|
||||||
|
case serialization::Status::ChannelIncompatible:
|
||||||
|
Ctx.Diags.diagnose(diagLoc, diag::serialization_module_incompatible_channel,
|
||||||
|
loadInfo.problematicChannel,
|
||||||
|
version::getCurrentCompilerChannel(),
|
||||||
|
ModuleName, moduleBufferID);
|
||||||
|
break;
|
||||||
case serialization::Status::Malformed:
|
case serialization::Status::Malformed:
|
||||||
Ctx.Diags.diagnose(diagLoc, diag::serialization_malformed_module,
|
Ctx.Diags.diagnose(diagLoc, diag::serialization_malformed_module,
|
||||||
moduleBufferID);
|
moduleBufferID);
|
||||||
@@ -1168,6 +1174,7 @@ void swift::serialization::diagnoseSerializedASTLoadFailureTransitive(
|
|||||||
case serialization::Status::NotInOSSA:
|
case serialization::Status::NotInOSSA:
|
||||||
case serialization::Status::NoncopyableGenericsMismatch:
|
case serialization::Status::NoncopyableGenericsMismatch:
|
||||||
case serialization::Status::RevisionIncompatible:
|
case serialization::Status::RevisionIncompatible:
|
||||||
|
case serialization::Status::ChannelIncompatible:
|
||||||
case serialization::Status::Malformed:
|
case serialization::Status::Malformed:
|
||||||
case serialization::Status::MalformedDocumentation:
|
case serialization::Status::MalformedDocumentation:
|
||||||
case serialization::Status::FailedToLoadBridgingHeader:
|
case serialization::Status::FailedToLoadBridgingHeader:
|
||||||
|
|||||||
89
test/Serialization/restrict-swiftmodule-to-channel.swift
Normal file
89
test/Serialization/restrict-swiftmodule-to-channel.swift
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Binary swiftmodules are restricted to a channel when set in the compiler.
|
||||||
|
|
||||||
|
// RUN: %empty-directory(%t/cache)
|
||||||
|
// RUN: %empty-directory(%t/build)
|
||||||
|
// RUN: split-file %s %t --leading-lines
|
||||||
|
|
||||||
|
//--- Lib.swift
|
||||||
|
public func foo() {}
|
||||||
|
|
||||||
|
/// Build Lib as a resilient and non-resilient swiftmodule
|
||||||
|
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 \
|
||||||
|
// RUN: -o %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||||
|
// RUN: -module-name ResilientLib -enable-library-evolution \
|
||||||
|
// RUN: -emit-module-interface-path %t/build/ResilientLib.swiftinterface
|
||||||
|
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 \
|
||||||
|
// RUN: -o %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||||
|
// RUN: -module-name NonResilientLib
|
||||||
|
|
||||||
|
/// Build a channel restricted Lib.
|
||||||
|
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
|
||||||
|
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 \
|
||||||
|
// RUN: -o %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||||
|
// RUN: -module-name ChannelLib -enable-library-evolution
|
||||||
|
|
||||||
|
|
||||||
|
/// 2. Test importing the non-resilient no-channel library from a channel compiler.
|
||||||
|
//--- NonResilientClient.swift
|
||||||
|
import NonResilientLib // expected-error {{compiled module was created for a different distribution channel '' than the local compiler 'restricted-channel', please ensure 'NonResilientLib' is found from the expected path:}}
|
||||||
|
foo()
|
||||||
|
|
||||||
|
/// Building a NonResilientLib client should reject the import for a tagged compiler
|
||||||
|
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
|
||||||
|
// RUN: %target-swift-frontend -typecheck %t/NonResilientClient.swift \
|
||||||
|
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||||
|
// RUN: -verify -verify-ignore-unknown
|
||||||
|
|
||||||
|
|
||||||
|
/// 3. Test importing the resilient no-channel library.
|
||||||
|
//--- ResilientClient.swift
|
||||||
|
import ResilientLib // expected-reject-error {{compiled module was created for a different distribution channel '' than the local compiler 'restricted-channel', please ensure 'ResilientLib' is found from the expected path:}}
|
||||||
|
// expected-rebuild-remark @-1 {{rebuilding module 'ResilientLib' from interface}}
|
||||||
|
// expected-rebuild-note @-2 {{compiled module is out of date}}
|
||||||
|
// expected-rebuild-note @-3 {{compiled for a different distribution channel}}
|
||||||
|
foo()
|
||||||
|
|
||||||
|
/// ResilientLib client should rebuild from swiftinterface when available.
|
||||||
|
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
|
||||||
|
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
|
||||||
|
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||||
|
// RUN: -verify -verify-additional-prefix rebuild- -Rmodule-interface-rebuild
|
||||||
|
|
||||||
|
// RUN: rm %t/build/ResilientLib.swiftinterface
|
||||||
|
// RUN: %empty-directory(%t/cache)
|
||||||
|
|
||||||
|
/// Building a ResilientLib client should succeed in no-channel / dev mode.
|
||||||
|
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
|
||||||
|
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
|
||||||
|
|
||||||
|
/// Building a ResilientLib client should reject the import for a channel compiler.
|
||||||
|
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
|
||||||
|
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
|
||||||
|
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||||
|
// RUN: -verify -verify-ignore-unknown -verify-additional-prefix reject-
|
||||||
|
|
||||||
|
/// Building a ResilientLib client should succeed for a channel compiler with SWIFT_IGNORE_SWIFTMODULE_REVISION
|
||||||
|
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel SWIFT_IGNORE_SWIFTMODULE_REVISION=true \
|
||||||
|
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
|
||||||
|
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
|
||||||
|
|
||||||
|
|
||||||
|
/// 4. Test importing the channel restricted library
|
||||||
|
//--- ChannelClient.swift
|
||||||
|
import ChannelLib // expected-reject-error {{compiled module was created for a different distribution channel 'restricted-channel' than the local compiler 'other-channel', please ensure 'ChannelLib' is found from the expected path}}
|
||||||
|
foo()
|
||||||
|
|
||||||
|
/// Importing ChannelLib should succeed with the same channel.
|
||||||
|
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
|
||||||
|
// RUN: %target-swift-frontend -typecheck %t/ChannelClient.swift \
|
||||||
|
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
|
||||||
|
|
||||||
|
/// Importing ChannelLib should succeed with a dev compiler.
|
||||||
|
// RUN: %target-swift-frontend -typecheck %t/ChannelClient.swift \
|
||||||
|
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
|
||||||
|
|
||||||
|
/// Importing ChannelLib from a different channel should be rejected.
|
||||||
|
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=other-channel \
|
||||||
|
// RUN: %target-swift-frontend -typecheck %t/ChannelClient.swift \
|
||||||
|
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||||
|
// RUN: -verify -verify-ignore-unknown -verify-additional-prefix reject-
|
||||||
Reference in New Issue
Block a user