[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;