Skip to content

Commit 423faed

Browse files
fanyang-monoMichalStrehovskylambdageek
authored
Error out when struct size is bigger than int.MaxValue (#104393)
* Error out when field size is too big * Error out during type loading and add a test * Move test to a different file * Make things loadable in the managed type system * Error out when size overflow the value that int could hold * Update the threshold to FIELD_OFFSET_LAST_REAL_OFFSET * Change the threashold to int.MaxValue * Fix mono struct size overflow issue * Add signed/unsigned type conversion * Use gint64 instead of long, due to windows * Update src/mono/mono/metadata/class-init.c Co-authored-by: Aleksey Kliger (λgeek) <[email protected]> * Fix test * Add CoreCLR test for Explicit struct * Address review feedback * Fixes Explicit struct size for Mono * Disable explicit struct tests on 32bit platforms, due to out of memory issue. * Disable CoreCLR test for NativeAOT and crossgen * Fix test * Add RequiresProcessIsolation for CoreCLR test --------- Co-authored-by: Michal Strehovský <[email protected]> Co-authored-by: Aleksey Kliger (λgeek) <[email protected]>
1 parent d315414 commit 423faed

File tree

11 files changed

+232
-7
lines changed

11 files changed

+232
-7
lines changed

src/coreclr/tools/Common/TypeSystem/Common/LayoutInt.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,20 @@ public string ToStringInvariant()
9292
return new LayoutInt(checked(left._value - right._value));
9393
}
9494

95+
public static LayoutInt AddThrowing(LayoutInt left, LayoutInt right, TypeDesc loadedType)
96+
{
97+
if (left.IsIndeterminate || right.IsIndeterminate)
98+
return Indeterminate;
99+
100+
int result = left._value + right._value;
101+
102+
// Overflow if both arguments have the opposite sign of the result
103+
if (((left._value ^ result) & (right._value ^ result)) < 0)
104+
ThrowHelper.ThrowTypeLoadException(loadedType);
105+
106+
return new LayoutInt(result);
107+
}
108+
95109
public override bool Equals(object obj)
96110
{
97111
if (obj is LayoutInt)

src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ protected ComputedInstanceFieldLayout ComputeSequentialFieldLayout(MetadataType
436436

437437
cumulativeInstanceFieldPos = AlignUpInstanceFieldOffset(cumulativeInstanceFieldPos, fieldSizeAndAlignment.Alignment, type.Context.Target);
438438
offsets[fieldOrdinal] = new FieldAndOffset(field, cumulativeInstanceFieldPos + offsetBias);
439-
cumulativeInstanceFieldPos = checked(cumulativeInstanceFieldPos + fieldSizeAndAlignment.Size);
439+
cumulativeInstanceFieldPos = LayoutInt.AddThrowing(cumulativeInstanceFieldPos, fieldSizeAndAlignment.Size, type);
440440

441441
fieldOrdinal++;
442442
}

src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections;
45
using System.Collections.Generic;
56

67
using Internal.TypeSystem;
@@ -306,11 +307,11 @@ public static bool CanCompareValueTypeBits(MetadataType type, MethodDesc objectE
306307

307308
private struct OverlappingFieldTracker
308309
{
309-
private bool[] _usedBytes;
310+
private BitArray _usedBytes;
310311

311312
public OverlappingFieldTracker(MetadataType type)
312313
{
313-
_usedBytes = new bool[type.InstanceFieldSize.AsInt];
314+
_usedBytes = new BitArray(type.InstanceFieldSize.AsInt);
314315
}
315316

316317
public bool TrackField(FieldDesc field)

src/coreclr/vm/methodtablebuilder.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,12 @@ MethodTableBuilder::BuildMethodTableThrowing(
18071807
}
18081808
}
18091809

1810+
if (IsValueClass())
1811+
{
1812+
if ((int)bmtFP->NumInstanceFieldBytes != (INT64)bmtFP->NumInstanceFieldBytes)
1813+
BuildMethodTableThrowException(IDS_CLASSLOAD_FIELDTOOLARGE);
1814+
}
1815+
18101816
if (CheckIfSIMDAndUpdateSize())
18111817
{
18121818
totalDeclaredFieldSize = bmtFP->NumInstanceFieldBytes;

src/mono/mono/metadata/class-init.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,10 @@ mono_class_setup_fields (MonoClass *klass)
310310

311311
/* Get the real size */
312312
explicit_size = mono_metadata_packing_from_typedef (klass->image, klass->type_token, &packing_size, &real_size);
313+
314+
if (real_size > GINT32_TO_UINT32(INT32_MAX - MONO_ABI_SIZEOF (MonoObject)))
315+
mono_class_set_type_load_failure (klass, "Can't load type %s. The size is too big.", m_class_get_name (klass));
316+
313317
if (explicit_size)
314318
instance_size += real_size;
315319

@@ -2322,7 +2326,12 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
23222326
}
23232327
/*TypeBuilders produce all sort of weird things*/
23242328
g_assert (image_is_dynamic (klass->image) || field_offsets [i] > 0);
2325-
real_size = field_offsets [i] + size;
2329+
2330+
gint64 raw_real_size = (gint64)field_offsets [i] + size;
2331+
real_size = (gint32)raw_real_size;
2332+
2333+
if (real_size != raw_real_size)
2334+
mono_class_set_type_load_failure (klass, "Can't load type %s. The size is too big.", m_class_get_name (klass));
23262335
}
23272336

23282337
instance_size = MAX (real_size, instance_size);
@@ -2381,7 +2390,13 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
23812390
/*
23822391
* Calc max size.
23832392
*/
2384-
real_size = MAX (real_size, size + field_offsets [i]);
2393+
gint64 raw_real_size = (gint64)field_offsets [i] + size;
2394+
gint32 real_size_cast = (gint32)raw_real_size;
2395+
2396+
if (real_size_cast != raw_real_size)
2397+
mono_class_set_type_load_failure (klass, "Can't load type %s. The size is too big.", m_class_get_name (klass));
2398+
2399+
real_size = MAX (real_size, real_size_cast);
23852400
}
23862401

23872402
/* 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_
25292544
}
25302545
}
25312546

2532-
/*valuetypes can't be neither bigger than 1Mb or empty. */
2533-
if (klass->valuetype && (klass->instance_size <= 0 || klass->instance_size > (0x100000 + MONO_ABI_SIZEOF (MonoObject)))) {
2547+
/*valuetypes can not be size 0 or bigger than 2gb. */
2548+
if (klass->valuetype && (klass->instance_size <= 0 || klass->instance_size > INT32_MAX)) {
25342549
/* Special case compiler generated types */
25352550
/* Hard to check for [CompilerGenerated] here */
25362551
if (!strstr (klass->name, "StaticArrayInitTypeSize") && !strstr (klass->name, "$ArrayType"))

src/tests/Loader/Loader.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<MergedWrapperProjectReference Remove="classloader/TypeGeneratorTests/**/*.??proj" />
1414
<MergedWrapperProjectReference Remove="classloader/StaticVirtualMethods/GenericContext/Generator/**/*.??proj" />
1515
<MergedWrapperProjectReference Remove="classloader/StaticVirtualMethods/TypeHierarchy/Generator/**/*.??proj" />
16+
<MergedWrapperProjectReference Remove="classloader/SequentialLayout/ManagedSequential/LargeStructSize_Mono.csproj" />
1617
</ItemGroup>
1718

1819
<Import Project="$(TestSourceDir)MergedTestRunner.targets" />
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
7+
using Xunit;
8+
9+
[SkipOnMono("This test suite tests CoreCLR and Crossgen2/NativeAOT-specific layout rules.")]
10+
public unsafe class LargeStructSize
11+
{
12+
struct X
13+
{
14+
byte x;
15+
BigArray a;
16+
}
17+
18+
[StructLayout(LayoutKind.Explicit)]
19+
struct X_explicit
20+
{
21+
[FieldOffset(0)]
22+
byte x;
23+
[FieldOffset(1)]
24+
BigArray a;
25+
}
26+
27+
[StructLayout(LayoutKind.Explicit)]
28+
struct X_non_blittable
29+
{
30+
[FieldOffset(0)]
31+
bool x;
32+
[FieldOffset(1)]
33+
BigArray a;
34+
}
35+
36+
struct Y
37+
{
38+
BigArray a;
39+
byte y;
40+
}
41+
42+
[StructLayout(LayoutKind.Explicit)]
43+
struct Y_explict
44+
{
45+
[FieldOffset(0)]
46+
BigArray b;
47+
[FieldOffset(int.MaxValue)]
48+
byte y;
49+
}
50+
51+
[StructLayout(LayoutKind.Sequential, Size = int.MaxValue)]
52+
struct BigArray
53+
{
54+
}
55+
56+
[Fact]
57+
public static void TestLargeStructSize()
58+
{
59+
Assert.Equal(int.MaxValue, sizeof(BigArray));
60+
Assert.Throws<TypeLoadException>(() => sizeof(X));
61+
Assert.Throws<TypeLoadException>(() => sizeof(Y));
62+
if (Environment.Is64BitProcess)
63+
{
64+
// Explicit struct of big size triggers out of memory error instead of type load exception
65+
Assert.Throws<TypeLoadException>(() => sizeof(X_explicit));
66+
Assert.Throws<TypeLoadException>(() => sizeof(X_non_blittable));
67+
Assert.Throws<TypeLoadException>(() => sizeof(Y_explict));
68+
}
69+
}
70+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
4+
<NativeAotIncompatible>true</NativeAotIncompatible>
5+
<CrossGenTest>false</CrossGenTest>
6+
<RequiresProcessIsolation>true</RequiresProcessIsolation>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<Compile Include="$(MSBuildProjectName).cs" />
10+
</ItemGroup>
11+
</Project>
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
7+
using Xunit;
8+
9+
public unsafe class LargeStructSize
10+
{
11+
struct X_64
12+
{
13+
byte x;
14+
BigArray_64_1 a;
15+
}
16+
17+
[StructLayout(LayoutKind.Explicit)]
18+
struct X_explicit_64
19+
{
20+
[FieldOffset(0)]
21+
bool x;
22+
[FieldOffset(1)]
23+
BigArray_64_1 a;
24+
}
25+
26+
struct Y_64
27+
{
28+
BigArray_64_1 a;
29+
byte y;
30+
}
31+
32+
struct X_32
33+
{
34+
byte x;
35+
BigArray_32_1 a;
36+
}
37+
38+
[StructLayout(LayoutKind.Explicit)]
39+
struct X_explicit_32
40+
{
41+
[FieldOffset(0)]
42+
bool x;
43+
[FieldOffset(1)]
44+
BigArray_32_1 a;
45+
}
46+
47+
struct Y_32
48+
{
49+
BigArray_32_1 a;
50+
byte y;
51+
}
52+
53+
[StructLayout(LayoutKind.Sequential, Size = int.MaxValue - 16)]
54+
struct BigArray_64_1
55+
{
56+
}
57+
58+
[StructLayout(LayoutKind.Sequential, Size = int.MaxValue - 16 - 1)]
59+
struct BigArray_64_2
60+
{
61+
}
62+
63+
[StructLayout(LayoutKind.Sequential, Size = int.MaxValue - 8)]
64+
struct BigArray_32_1
65+
{
66+
}
67+
68+
[StructLayout(LayoutKind.Sequential, Size = int.MaxValue - 8 - 1)]
69+
struct BigArray_32_2
70+
{
71+
}
72+
73+
[Fact]
74+
public static void TestLargeStructSize()
75+
{
76+
if (Environment.Is64BitProcess)
77+
{
78+
Assert.Equal(int.MaxValue - (IntPtr.Size * 2), sizeof(BigArray_64_1));
79+
Assert.Throws<TypeLoadException>(() => sizeof(BigArray_64_2));
80+
Assert.Throws<TypeLoadException>(() => sizeof(X_64));
81+
Assert.Throws<TypeLoadException>(() => sizeof(X_explicit_64));
82+
Assert.Throws<TypeLoadException>(() => sizeof(Y_64));
83+
}
84+
else
85+
{
86+
Assert.Equal(int.MaxValue - (IntPtr.Size * 2), sizeof(BigArray_32_1));
87+
Assert.Throws<TypeLoadException>(() => sizeof(BigArray_32_2));
88+
Assert.Throws<TypeLoadException>(() => sizeof(X_32));
89+
Assert.Throws<TypeLoadException>(() => sizeof(X_explicit_32));
90+
Assert.Throws<TypeLoadException>(() => sizeof(Y_32));
91+
}
92+
}
93+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<RequiresProcessIsolation>true</RequiresProcessIsolation>
4+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
5+
<NativeAotIncompatible>true</NativeAotIncompatible>
6+
<CrossGenTest>false</CrossGenTest>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<Compile Include="$(MSBuildProjectName).cs" />
10+
</ItemGroup>
11+
</Project>

0 commit comments

Comments
 (0)