From cca5f82aa0eb3020ea0d40e62670d8028b52c406 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 30 Sep 2021 16:01:57 +0000 Subject: [PATCH] Handle container builders capacity overflow --- .../kotlin/kotlin/collections/ArrayList.kt | 1 + .../main/kotlin/kotlin/collections/HashMap.kt | 1 + .../collections/builders/ListBuilder.kt | 1 + .../kotlin/collections/builders/MapBuilder.kt | 1 + .../jvm/test/collections/ListBuilderTest.kt | 22 ++++++++-- .../jvm/test/collections/MapBuilderTest.kt | 40 +++++++++++++++++++ 6 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 libraries/stdlib/jvm/test/collections/MapBuilderTest.kt diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt index 068bb0cc6d6..b53896dac01 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt @@ -143,6 +143,7 @@ actual class ArrayList private constructor( final actual fun ensureCapacity(minCapacity: Int) { if (backing != null) throw IllegalStateException() // just in case somebody casts subList to ArrayList + if (minCapacity < 0) throw OutOfMemoryError() // overflow if (minCapacity > array.size) { val newSize = ArrayDeque.newCapacity(array.size, minCapacity) array = array.copyOfUninitializedElements(newSize) diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/collections/HashMap.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/collections/HashMap.kt index 8f86d1c17ab..7cf788a66c6 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/collections/HashMap.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/collections/HashMap.kt @@ -180,6 +180,7 @@ actual class HashMap private constructor( } private fun ensureCapacity(capacity: Int) { + if (capacity < 0) throw OutOfMemoryError() // overflow if (capacity > this.capacity) { var newSize = this.capacity * 3 / 2 if (capacity > newSize) newSize = capacity diff --git a/libraries/stdlib/jvm/src/kotlin/collections/builders/ListBuilder.kt b/libraries/stdlib/jvm/src/kotlin/collections/builders/ListBuilder.kt index eef8cd704ed..d0e32bf32db 100644 --- a/libraries/stdlib/jvm/src/kotlin/collections/builders/ListBuilder.kt +++ b/libraries/stdlib/jvm/src/kotlin/collections/builders/ListBuilder.kt @@ -178,6 +178,7 @@ internal class ListBuilder private constructor( private fun ensureCapacity(minCapacity: Int) { if (backing != null) throw IllegalStateException() // just in case somebody casts subList to ListBuilder + if (minCapacity < 0) throw OutOfMemoryError() // overflow if (minCapacity > array.size) { val newSize = ArrayDeque.newCapacity(array.size, minCapacity) array = array.copyOfUninitializedElements(newSize) diff --git a/libraries/stdlib/jvm/src/kotlin/collections/builders/MapBuilder.kt b/libraries/stdlib/jvm/src/kotlin/collections/builders/MapBuilder.kt index d90c4d538d6..a807cec9b4c 100644 --- a/libraries/stdlib/jvm/src/kotlin/collections/builders/MapBuilder.kt +++ b/libraries/stdlib/jvm/src/kotlin/collections/builders/MapBuilder.kt @@ -177,6 +177,7 @@ internal class MapBuilder private constructor( } private fun ensureCapacity(capacity: Int) { + if (capacity < 0) throw OutOfMemoryError() // overflow if (capacity > this.capacity) { var newSize = this.capacity * 3 / 2 if (capacity > newSize) newSize = capacity diff --git a/libraries/stdlib/jvm/test/collections/ListBuilderTest.kt b/libraries/stdlib/jvm/test/collections/ListBuilderTest.kt index 8c3b024fa5d..08c679c21e4 100644 --- a/libraries/stdlib/jvm/test/collections/ListBuilderTest.kt +++ b/libraries/stdlib/jvm/test/collections/ListBuilderTest.kt @@ -6,9 +6,7 @@ package test.collections import kotlin.collections.builders.* -import kotlin.test.Test -import kotlin.test.assertSame -import kotlin.test.assertTrue +import kotlin.test.* @Suppress("INVISIBLE_MEMBER") class ListBuilderTest { @@ -57,4 +55,22 @@ class ListBuilderTest { testToArray(numberOfElements + 1) { (this as java.util.Collection).toArray(it) } testToArray(numberOfElements + 2) { (this as java.util.Collection).toArray(it) } } + + @Test + fun capacityOverflow() { + val builderSize = 15 + val giantListSize = Int.MAX_VALUE - builderSize + 1 + + val giantList = object : AbstractList() { + override val size: Int get() = giantListSize + override fun get(index: Int): String = "element" + } + + buildList { + repeat(builderSize) { add("element") } + + assertFails { addAll(giantList) } + assertEquals(builderSize, size) + } + } } \ No newline at end of file diff --git a/libraries/stdlib/jvm/test/collections/MapBuilderTest.kt b/libraries/stdlib/jvm/test/collections/MapBuilderTest.kt new file mode 100644 index 00000000000..bd9be54c8a0 --- /dev/null +++ b/libraries/stdlib/jvm/test/collections/MapBuilderTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package test.collections + +import kotlin.test.assertEquals +import kotlin.test.assertFails +import kotlin.test.Test + +class MapBuilderTest { + + @Test + fun capacityOverflow() { + val builderSize = 15 + val giantMapSize = Int.MAX_VALUE - builderSize + 1 + + val giantMap = object : AbstractMap() { + override val entries: Set> = object : AbstractSet>() { + override val size: Int get() = giantMapSize + override fun iterator(): Iterator> { + return indexSequence().map { + object : Map.Entry { + override val key: Int get() = it + override val value: String get() = "value" + } + }.take(size).iterator() + } + } + } + + buildMap { + repeat(builderSize) { put(-it, "value") } + + assertFails { putAll(giantMap) } + assertEquals(builderSize, size) + } + } +} \ No newline at end of file