diff --git a/src/coreclr/tools/Common/TypeSystem/Common/LayoutInt.cs b/src/coreclr/tools/Common/TypeSystem/Common/LayoutInt.cs
index a2067d042252c..c533d9d12f0e8 100644
--- a/src/coreclr/tools/Common/TypeSystem/Common/LayoutInt.cs
+++ b/src/coreclr/tools/Common/TypeSystem/Common/LayoutInt.cs
@@ -92,6 +92,20 @@ public string ToStringInvariant()
return new LayoutInt(checked(left._value - right._value));
}
+ public static LayoutInt AddThrowing(LayoutInt left, LayoutInt right, TypeDesc loadedType)
+ {
+ if (left.IsIndeterminate || right.IsIndeterminate)
+ return Indeterminate;
+
+ int result = left._value + right._value;
+
+ // Overflow if both arguments have the opposite sign of the result
+ if (((left._value ^ result) & (right._value ^ result)) < 0)
+ ThrowHelper.ThrowTypeLoadException(loadedType);
+
+ return new LayoutInt(result);
+ }
+
public override bool Equals(object obj)
{
if (obj is LayoutInt)
diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
index ff85e36436176..4bd6a3cb7a952 100644
--- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
+++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
@@ -436,7 +436,7 @@ protected ComputedInstanceFieldLayout ComputeSequentialFieldLayout(MetadataType
cumulativeInstanceFieldPos = AlignUpInstanceFieldOffset(cumulativeInstanceFieldPos, fieldSizeAndAlignment.Alignment, type.Context.Target);
offsets[fieldOrdinal] = new FieldAndOffset(field, cumulativeInstanceFieldPos + offsetBias);
- cumulativeInstanceFieldPos = checked(cumulativeInstanceFieldPos + fieldSizeAndAlignment.Size);
+ cumulativeInstanceFieldPos = LayoutInt.AddThrowing(cumulativeInstanceFieldPos, fieldSizeAndAlignment.Size, type);
fieldOrdinal++;
}
diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs
index e62d825af5a11..a314dd3917bae 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Collections;
using System.Collections.Generic;
using Internal.TypeSystem;
@@ -306,11 +307,11 @@ public static bool CanCompareValueTypeBits(MetadataType type, MethodDesc objectE
private struct OverlappingFieldTracker
{
- private bool[] _usedBytes;
+ private BitArray _usedBytes;
public OverlappingFieldTracker(MetadataType type)
{
- _usedBytes = new bool[type.InstanceFieldSize.AsInt];
+ _usedBytes = new BitArray(type.InstanceFieldSize.AsInt);
}
public bool TrackField(FieldDesc field)
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index bcc88d55b8cc3..5d2f1f1589130 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -1807,6 +1807,12 @@ MethodTableBuilder::BuildMethodTableThrowing(
}
}
+ if (IsValueClass())
+ {
+ if ((int)bmtFP->NumInstanceFieldBytes != (INT64)bmtFP->NumInstanceFieldBytes)
+ BuildMethodTableThrowException(IDS_CLASSLOAD_FIELDTOOLARGE);
+ }
+
if (CheckIfSIMDAndUpdateSize())
{
totalDeclaredFieldSize = bmtFP->NumInstanceFieldBytes;
diff --git a/src/mono/mono/metadata/class-init.c b/src/mono/mono/metadata/class-init.c
index 76459536c2497..91f4040334616 100644
--- a/src/mono/mono/metadata/class-init.c
+++ b/src/mono/mono/metadata/class-init.c
@@ -310,6 +310,10 @@ mono_class_setup_fields (MonoClass *klass)
/* Get the real size */
explicit_size = mono_metadata_packing_from_typedef (klass->image, klass->type_token, &packing_size, &real_size);
+
+ if (real_size > GINT32_TO_UINT32(INT32_MAX - MONO_ABI_SIZEOF (MonoObject)))
+ mono_class_set_type_load_failure (klass, "Can't load type %s. The size is too big.", m_class_get_name (klass));
+
if (explicit_size)
instance_size += real_size;
@@ -2322,7 +2326,12 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
}
/*TypeBuilders produce all sort of weird things*/
g_assert (image_is_dynamic (klass->image) || field_offsets [i] > 0);
- real_size = field_offsets [i] + size;
+
+ gint64 raw_real_size = (gint64)field_offsets [i] + size;
+ real_size = (gint32)raw_real_size;
+
+ if (real_size != raw_real_size)
+ mono_class_set_type_load_failure (klass, "Can't load type %s. The size is too big.", m_class_get_name (klass));
}
instance_size = MAX (real_size, instance_size);
@@ -2381,7 +2390,13 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
/*
* Calc max size.
*/
- real_size = MAX (real_size, size + field_offsets [i]);
+ gint64 raw_real_size = (gint64)field_offsets [i] + size;
+ gint32 real_size_cast = (gint32)raw_real_size;
+
+ if (real_size_cast != raw_real_size)
+ mono_class_set_type_load_failure (klass, "Can't load type %s. The size is too big.", m_class_get_name (klass));
+
+ real_size = MAX (real_size, real_size_cast);
}
/* check for incorrectly aligned or overlapped by a non-object field */
@@ -2529,8 +2544,8 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
}
}
- /*valuetypes can't be neither bigger than 1Mb or empty. */
- if (klass->valuetype && (klass->instance_size <= 0 || klass->instance_size > (0x100000 + MONO_ABI_SIZEOF (MonoObject)))) {
+ /*valuetypes can not be size 0 or bigger than 2gb. */
+ if (klass->valuetype && (klass->instance_size <= 0 || klass->instance_size > INT32_MAX)) {
/* Special case compiler generated types */
/* Hard to check for [CompilerGenerated] here */
if (!strstr (klass->name, "StaticArrayInitTypeSize") && !strstr (klass->name, "$ArrayType"))
diff --git a/src/tests/Loader/Loader.csproj b/src/tests/Loader/Loader.csproj
index 31ffe922e6c74..3c4853a06bfc0 100644
--- a/src/tests/Loader/Loader.csproj
+++ b/src/tests/Loader/Loader.csproj
@@ -13,6 +13,7 @@
+
diff --git a/src/tests/Loader/classloader/SequentialLayout/ManagedSequential/LargeStructSize_CoreCLR.cs b/src/tests/Loader/classloader/SequentialLayout/ManagedSequential/LargeStructSize_CoreCLR.cs
new file mode 100644
index 0000000000000..0588a19265b7a
--- /dev/null
+++ b/src/tests/Loader/classloader/SequentialLayout/ManagedSequential/LargeStructSize_CoreCLR.cs
@@ -0,0 +1,70 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Xunit;
+
+[SkipOnMono("This test suite tests CoreCLR and Crossgen2/NativeAOT-specific layout rules.")]
+public unsafe class LargeStructSize
+{
+ struct X
+ {
+ byte x;
+ BigArray a;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ struct X_explicit
+ {
+ [FieldOffset(0)]
+ byte x;
+ [FieldOffset(1)]
+ BigArray a;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ struct X_non_blittable
+ {
+ [FieldOffset(0)]
+ bool x;
+ [FieldOffset(1)]
+ BigArray a;
+ }
+
+ struct Y
+ {
+ BigArray a;
+ byte y;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ struct Y_explict
+ {
+ [FieldOffset(0)]
+ BigArray b;
+ [FieldOffset(int.MaxValue)]
+ byte y;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Size = int.MaxValue)]
+ struct BigArray
+ {
+ }
+
+ [Fact]
+ public static void TestLargeStructSize()
+ {
+ Assert.Equal(int.MaxValue, sizeof(BigArray));
+ Assert.Throws(() => sizeof(X));
+ Assert.Throws(() => sizeof(Y));
+ if (Environment.Is64BitProcess)
+ {
+ // Explicit struct of big size triggers out of memory error instead of type load exception
+ Assert.Throws(() => sizeof(X_explicit));
+ Assert.Throws(() => sizeof(X_non_blittable));
+ Assert.Throws(() => sizeof(Y_explict));
+ }
+ }
+}
diff --git a/src/tests/Loader/classloader/SequentialLayout/ManagedSequential/LargeStructSize_CoreCLR.csproj b/src/tests/Loader/classloader/SequentialLayout/ManagedSequential/LargeStructSize_CoreCLR.csproj
new file mode 100644
index 0000000000000..a13bdc9995d53
--- /dev/null
+++ b/src/tests/Loader/classloader/SequentialLayout/ManagedSequential/LargeStructSize_CoreCLR.csproj
@@ -0,0 +1,11 @@
+
+
+ true
+ true
+ false
+ true
+
+
+
+
+
diff --git a/src/tests/Loader/classloader/SequentialLayout/ManagedSequential/LargeStructSize_Mono.cs b/src/tests/Loader/classloader/SequentialLayout/ManagedSequential/LargeStructSize_Mono.cs
new file mode 100644
index 0000000000000..f99357a223552
--- /dev/null
+++ b/src/tests/Loader/classloader/SequentialLayout/ManagedSequential/LargeStructSize_Mono.cs
@@ -0,0 +1,93 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Xunit;
+
+public unsafe class LargeStructSize
+{
+ struct X_64
+ {
+ byte x;
+ BigArray_64_1 a;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ struct X_explicit_64
+ {
+ [FieldOffset(0)]
+ bool x;
+ [FieldOffset(1)]
+ BigArray_64_1 a;
+ }
+
+ struct Y_64
+ {
+ BigArray_64_1 a;
+ byte y;
+ }
+
+ struct X_32
+ {
+ byte x;
+ BigArray_32_1 a;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ struct X_explicit_32
+ {
+ [FieldOffset(0)]
+ bool x;
+ [FieldOffset(1)]
+ BigArray_32_1 a;
+ }
+
+ struct Y_32
+ {
+ BigArray_32_1 a;
+ byte y;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Size = int.MaxValue - 16)]
+ struct BigArray_64_1
+ {
+ }
+
+ [StructLayout(LayoutKind.Sequential, Size = int.MaxValue - 16 - 1)]
+ struct BigArray_64_2
+ {
+ }
+
+ [StructLayout(LayoutKind.Sequential, Size = int.MaxValue - 8)]
+ struct BigArray_32_1
+ {
+ }
+
+ [StructLayout(LayoutKind.Sequential, Size = int.MaxValue - 8 - 1)]
+ struct BigArray_32_2
+ {
+ }
+
+ [Fact]
+ public static void TestLargeStructSize()
+ {
+ if (Environment.Is64BitProcess)
+ {
+ Assert.Equal(int.MaxValue - (IntPtr.Size * 2), sizeof(BigArray_64_1));
+ Assert.Throws(() => sizeof(BigArray_64_2));
+ Assert.Throws(() => sizeof(X_64));
+ Assert.Throws(() => sizeof(X_explicit_64));
+ Assert.Throws(() => sizeof(Y_64));
+ }
+ else
+ {
+ Assert.Equal(int.MaxValue - (IntPtr.Size * 2), sizeof(BigArray_32_1));
+ Assert.Throws(() => sizeof(BigArray_32_2));
+ Assert.Throws(() => sizeof(X_32));
+ Assert.Throws(() => sizeof(X_explicit_32));
+ Assert.Throws(() => sizeof(Y_32));
+ }
+ }
+}
diff --git a/src/tests/Loader/classloader/SequentialLayout/ManagedSequential/LargeStructSize_Mono.csproj b/src/tests/Loader/classloader/SequentialLayout/ManagedSequential/LargeStructSize_Mono.csproj
new file mode 100644
index 0000000000000..3ce172359e290
--- /dev/null
+++ b/src/tests/Loader/classloader/SequentialLayout/ManagedSequential/LargeStructSize_Mono.csproj
@@ -0,0 +1,11 @@
+
+
+ true
+ true
+ true
+ false
+
+
+
+
+
diff --git a/src/tests/issues.targets b/src/tests/issues.targets
index 078ca36547d11..5ba64b23db2fb 100644
--- a/src/tests/issues.targets
+++ b/src/tests/issues.targets
@@ -27,6 +27,9 @@
This test is to verify we are running mono, and therefore only makes sense on mono.
+
+ This test is designed to run on mono only.
+
https://github.com/dotnet/runtime/issues/83658