[K/N] Fix large array allocations ^KT-54659 If the count argument is above 2**29, then memberSize does not fit in uint32_t. Co-authored-by: Troels Lund <troels@google.com> Merge-request: KOTLIN-MR-533 Merged-by: Alexander Shabalin <alexander.shabalin@jetbrains.com>
diff --git a/kotlin-native/runtime/src/gc/cms/cpp/GCImpl.cpp b/kotlin-native/runtime/src/gc/cms/cpp/GCImpl.cpp index 114efb3..107752e 100644 --- a/kotlin-native/runtime/src/gc/cms/cpp/GCImpl.cpp +++ b/kotlin-native/runtime/src/gc/cms/cpp/GCImpl.cpp
@@ -54,7 +54,7 @@ return impl_->objectFactoryThreadQueue().CreateObject(typeInfo); } -ALWAYS_INLINE ArrayHeader* gc::GC::ThreadData::CreateArray(const TypeInfo* typeInfo, uint32_t elements) noexcept { +ALWAYS_INLINE ArrayHeader* gc::GC::ThreadData::CreateArray(const TypeInfo* typeInfo, uint32_t elements) { return impl_->objectFactoryThreadQueue().CreateArray(typeInfo, elements); }
diff --git a/kotlin-native/runtime/src/gc/common/cpp/GC.hpp b/kotlin-native/runtime/src/gc/common/cpp/GC.hpp index 742dcb3..d583b3c 100644 --- a/kotlin-native/runtime/src/gc/common/cpp/GC.hpp +++ b/kotlin-native/runtime/src/gc/common/cpp/GC.hpp
@@ -42,7 +42,7 @@ void ClearForTests() noexcept; ObjHeader* CreateObject(const TypeInfo* typeInfo) noexcept; - ArrayHeader* CreateArray(const TypeInfo* typeInfo, uint32_t elements) noexcept; + ArrayHeader* CreateArray(const TypeInfo* typeInfo, uint32_t elements); void OnStoppedForGC() noexcept; void OnSuspendForGC() noexcept;
diff --git a/kotlin-native/runtime/src/gc/noop/cpp/GCImpl.cpp b/kotlin-native/runtime/src/gc/noop/cpp/GCImpl.cpp index dfca326..1a71799 100644 --- a/kotlin-native/runtime/src/gc/noop/cpp/GCImpl.cpp +++ b/kotlin-native/runtime/src/gc/noop/cpp/GCImpl.cpp
@@ -44,7 +44,7 @@ return impl_->objectFactoryThreadQueue().CreateObject(typeInfo); } -ALWAYS_INLINE ArrayHeader* gc::GC::ThreadData::CreateArray(const TypeInfo* typeInfo, uint32_t elements) noexcept { +ALWAYS_INLINE ArrayHeader* gc::GC::ThreadData::CreateArray(const TypeInfo* typeInfo, uint32_t elements) { return impl_->objectFactoryThreadQueue().CreateArray(typeInfo, elements); }
diff --git a/kotlin-native/runtime/src/gc/stms/cpp/GCImpl.cpp b/kotlin-native/runtime/src/gc/stms/cpp/GCImpl.cpp index fbb33f6..a10c025 100644 --- a/kotlin-native/runtime/src/gc/stms/cpp/GCImpl.cpp +++ b/kotlin-native/runtime/src/gc/stms/cpp/GCImpl.cpp
@@ -57,7 +57,7 @@ return impl_->objectFactoryThreadQueue().CreateObject(typeInfo); } -ALWAYS_INLINE ArrayHeader* gc::GC::ThreadData::CreateArray(const TypeInfo* typeInfo, uint32_t elements) noexcept { +ALWAYS_INLINE ArrayHeader* gc::GC::ThreadData::CreateArray(const TypeInfo* typeInfo, uint32_t elements) { return impl_->objectFactoryThreadQueue().CreateArray(typeInfo, elements); }
diff --git a/kotlin-native/runtime/src/mm/cpp/ObjectFactory.hpp b/kotlin-native/runtime/src/mm/cpp/ObjectFactory.hpp index b3440e8..51d3106 100644 --- a/kotlin-native/runtime/src/mm/cpp/ObjectFactory.hpp +++ b/kotlin-native/runtime/src/mm/cpp/ObjectFactory.hpp
@@ -12,6 +12,7 @@ #include <type_traits> #include "Alignment.hpp" +#include "Exceptions.h" #include "FinalizerHooks.hpp" #include "Memory.h" #include "Mutex.hpp" @@ -533,9 +534,17 @@ return Storage::Node::GetSizeForDataSize(allocSize); } - ArrayHeader* CreateArray(const TypeInfo* typeInfo, uint32_t count) noexcept { + ArrayHeader* CreateArray(const TypeInfo* typeInfo, uint32_t count) { RuntimeAssert(typeInfo->IsArray(), "Must be an array"); size_t allocSize = ArrayAllocatedDataSize(typeInfo, count); + // On 32-bit systems, overflow can happen in several places along + // the size calculation. If overflow did not happen in the + // multiplication checked in the previous call, but any of the + // subsequent additions overflowed, then the overflowed value will + // be small compared to the number of entries in the array. + if (Storage::Node::GetSizeForDataSize(allocSize) < count) { + ThrowOutOfMemoryError(); + } auto& node = producer_.Insert(allocSize); auto* heapArray = new (node.Data()) HeapArrayHeader(); auto* array = &heapArray->array; @@ -559,7 +568,10 @@ } static size_t ArrayAllocatedDataSize(const TypeInfo* typeInfo, uint32_t count) noexcept { - uint32_t membersSize = static_cast<uint32_t>(-typeInfo->instanceSize_) * count; + size_t membersSize; + if (__builtin_mul_overflow(static_cast<size_t>(-typeInfo->instanceSize_), count, &membersSize)) { + ThrowOutOfMemoryError(); + } // Note: array body is aligned, but for size computation it is enough to align the sum. return AlignUp(sizeof(HeapArrayHeader) + membersSize, kObjectAlignment); }
diff --git a/kotlin-native/runtime/src/mm/cpp/ObjectOps.cpp b/kotlin-native/runtime/src/mm/cpp/ObjectOps.cpp index b72df3f..ebf6e9e 100644 --- a/kotlin-native/runtime/src/mm/cpp/ObjectOps.cpp +++ b/kotlin-native/runtime/src/mm/cpp/ObjectOps.cpp
@@ -62,7 +62,7 @@ RETURN_OBJ(object); } -OBJ_GETTER(mm::AllocateArray, ThreadData* threadData, const TypeInfo* typeInfo, uint32_t elements) noexcept { +OBJ_GETTER(mm::AllocateArray, ThreadData* threadData, const TypeInfo* typeInfo, uint32_t elements) { AssertThreadState(threadData, ThreadState::kRunnable); // TODO: Make this work with GCs that can stop thread at any point. auto* array = threadData->gc().CreateArray(typeInfo, static_cast<uint32_t>(elements));
diff --git a/kotlin-native/runtime/src/mm/cpp/ObjectOps.hpp b/kotlin-native/runtime/src/mm/cpp/ObjectOps.hpp index 9e06fa8..8673228 100644 --- a/kotlin-native/runtime/src/mm/cpp/ObjectOps.hpp +++ b/kotlin-native/runtime/src/mm/cpp/ObjectOps.hpp
@@ -27,7 +27,7 @@ OBJ_GETTER(ReadHeapRefAtomic, ObjHeader** location) noexcept; OBJ_GETTER(CompareAndSwapHeapRef, ObjHeader** location, ObjHeader* expected, ObjHeader* value) noexcept; OBJ_GETTER(AllocateObject, ThreadData* threadData, const TypeInfo* typeInfo) noexcept; -OBJ_GETTER(AllocateArray, ThreadData* threadData, const TypeInfo* typeInfo, uint32_t elements) noexcept; +OBJ_GETTER(AllocateArray, ThreadData* threadData, const TypeInfo* typeInfo, uint32_t elements); // This does not take into account how much storage did the underlying allocator (malloc/mimalloc) reserved. size_t GetAllocatedHeapSize(ObjHeader* object) noexcept;