From af23706a9ad18a492c8354ecf1a01d7b404f7bcd Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Fri, 8 Dec 2023 16:53:42 +0100 Subject: [PATCH] Do not allow writing metadata versions that are too high because a format may be incorrect for future compilers #KT-64230 Fixed --- .../kotlinx/metadata/jvm/JvmMetadataVersion.kt | 3 +++ .../metadata/jvm/KotlinClassMetadata.kt | 17 ++++++++++------- .../metadata/test/WritersContractTest.kt | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/JvmMetadataVersion.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/JvmMetadataVersion.kt index 3306179df89..ac7b0b70f1b 100644 --- a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/JvmMetadataVersion.kt +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/JvmMetadataVersion.kt @@ -112,5 +112,8 @@ public class JvmMetadataVersion(public val major: Int, public val minor: Int, pu */ @JvmField public val LATEST_STABLE_SUPPORTED: JvmMetadataVersion = JvmMetadataVersion(CompilerMetadataVersion.INSTANCE.toArray()) + + @JvmField + internal val HIGHEST_ALLOWED_TO_WRITE: JvmMetadataVersion = JvmMetadataVersion(CompilerMetadataVersion.INSTANCE_NEXT.toArray()) } } diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt index c41dd04dfa6..25af3584376 100644 --- a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt @@ -106,7 +106,7 @@ public sealed class KotlinClassMetadata { override fun write(): Metadata { throwIfNotWriteable(isAllowedToWrite, "class") - checkMetadataVersion(version) + checkMetadataVersionForWrite(version) return wrapWriteIntoIAE { val writer = ClassWriter(JvmStringTable()) writer.writeClass(kmClass) @@ -198,7 +198,7 @@ public sealed class KotlinClassMetadata { override fun write(): Metadata { throwIfNotWriteable(isAllowedToWrite, "file facade") - checkMetadataVersion(version) + checkMetadataVersionForWrite(version) return wrapWriteIntoIAE { val writer = PackageWriter(JvmStringTable()) writer.writePackage(kmPackage) @@ -286,7 +286,7 @@ public sealed class KotlinClassMetadata { override fun write(): Metadata { throwIfNotWriteable(isAllowedToWrite, if (isLambda) "lambda" else "synthetic class") - checkMetadataVersion(version) + checkMetadataVersionForWrite(version) return if (isLambda) wrapWriteIntoIAE { val writer = LambdaWriter(JvmStringTable()) writer.writeLambda(kmLambda!!) @@ -424,7 +424,7 @@ public sealed class KotlinClassMetadata { override fun write(): Metadata { throwIfNotWriteable(isAllowedToWrite, "multi-file class facade") - checkMetadataVersion(version) + checkMetadataVersionForWrite(version) return Metadata( MULTI_FILE_CLASS_FACADE_KIND, version.toIntArray(), partClassNames.toTypedArray(), extraInt = flags @@ -506,7 +506,7 @@ public sealed class KotlinClassMetadata { override fun write(): Metadata { throwIfNotWriteable(isAllowedToWrite, "multi-file class part") - checkMetadataVersion(version) + checkMetadataVersionForWrite(version) return wrapWriteIntoIAE { val writer = PackageWriter(JvmStringTable()) writer.writePackage(kmPackage) @@ -586,7 +586,7 @@ public sealed class KotlinClassMetadata { override fun write(): Metadata { throwIfNotWriteable(!lenient, "unknown kind") - checkMetadataVersion(version) + checkMetadataVersionForWrite(version) return Metadata( original.kind, version.toIntArray(), @@ -817,11 +817,14 @@ public sealed class KotlinClassMetadata { throw IllegalArgumentException("This $name cannot be written because it represents metadata read in lenient mode") } - internal fun checkMetadataVersion(version: JvmMetadataVersion) { + private fun checkMetadataVersionForWrite(version: JvmMetadataVersion) { require(version.major >= 1 && (version.major > 1 || version.minor >= 4)) { "This version of kotlinx-metadata-jvm doesn't support writing Kotlin metadata of version earlier than 1.4. " + "Please change the version from $version to at least [1, 4]." } + require(version <= JvmMetadataVersion.HIGHEST_ALLOWED_TO_WRITE) { + "kotlinx-metadata-jvm cannot write metadata for future compiler versions. Requested to write version $version, but highest known version is ${JvmMetadataVersion.HIGHEST_ALLOWED_TO_WRITE}" + } } /** diff --git a/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/WritersContractTest.kt b/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/WritersContractTest.kt index a85e7f7ab1c..d54160592f2 100644 --- a/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/WritersContractTest.kt +++ b/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/WritersContractTest.kt @@ -48,4 +48,22 @@ class WritersContractTest { assertEquals(before.extraString, after.extraString) assertEquals(before.packageName, after.packageName) } + + @Test + fun futureVersionCantBeWritten() = everyType.forEach { before -> + val md = KotlinClassMetadata.readStrict(before) + md.version = JvmMetadataVersion(3, 4, 5) + assertFailsWith { md.write() } + } + + @Test + fun nextVersionWrite() = everyType.forEach { before -> + val md = KotlinClassMetadata.readStrict(before) + val ver = md.version + assertEquals(2, ver.major) // to correctly handle future case 2.x -> 3.0 + md.version = JvmMetadataVersion(ver.major, ver.minor + 1, ver.patch) + md.write() // OK + md.version = JvmMetadataVersion(ver.major, ver.minor + 2, ver.patch) + assertFailsWith { md.write() } + } }