Skip to content

Commit 6773a92

Browse files
committed
Disallow arrays of System.Void
The CoreCLR type loader allowed creating arrays of System.Void. Many operations with these invalid array types failed, often in inscrutable ways. For example, GetInterfaces() call failed with type load exception of IEnumerable type. The exact failure modes are different between runtimes. It is better to disallow creating these invalid array types in the first place, across all runtimes, to make the behavior robust and consistent. Related to #88620 Fixes #94086
1 parent 45978bd commit 6773a92

File tree

14 files changed

+30
-89
lines changed

14 files changed

+30
-89
lines changed

src/coreclr/dlls/mscorrc/mscorrc.rc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ BEGIN
325325
IDS_CLASSLOAD_BADFORMAT "Could not load type '%1' from assembly '%2' because the format is invalid."
326326
IDS_CLASSLOAD_BYREFARRAY "Could not create array type '%1' from assembly '%2' because the element type is ByRef."
327327
IDS_CLASSLOAD_BYREFLIKEARRAY "Could not create array type '%1' from assembly '%2' because the element type is ByRef-like."
328+
IDS_CLASSLOAD_VOIDARRAY "Could not create array type '%1' from assembly '%2' because the element type is System.Void."
328329
IDS_CLASSLOAD_FIELDTOOLARGE "Size of field of type '%1' from assembly '%2' is too large."
329330
IDS_CLASSLOAD_CANTEXTEND "Type '%1' from assembly '%2' cannot extend from any other type."
330331
IDS_CLASSLOAD_STATICVIRTUAL "Method '%3' in type '%1' from assembly '%2' cannot be a static and a virtual."

src/coreclr/dlls/mscorrc/resource.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@
123123
#define IDS_CLASSLOAD_BADFORMAT 0x1774
124124
#define IDS_CLASSLOAD_BYREFARRAY 0x1775
125125
#define IDS_CLASSLOAD_BYREFLIKEARRAY 0x1776
126+
#define IDS_CLASSLOAD_VOIDARRAY 0x1777
126127
#define IDS_CLASSLOAD_STATICVIRTUAL 0x1778
127128
#define IDS_CLASSLOAD_REDUCEACCESS 0x1779
128129
#define IDS_CLASSLOAD_BADPINVOKE 0x177a

src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,6 @@
423423
<Compile Include="System\Reflection\Runtime\General\MetadataReaderExtensions.NativeFormat.cs" />
424424
<Compile Include="System\Reflection\Runtime\General\NamespaceChain.cs" />
425425
<Compile Include="System\Reflection\Runtime\General\NativeFormat\DefaultValueParser.cs" />
426-
<Compile Include="System\Reflection\Runtime\General\RuntimeTypeHandleKey.cs" />
427426
<Compile Include="System\Reflection\Runtime\General\ThunkedApis.cs" />
428427
<Compile Include="System\Reflection\Runtime\General\TypeContext.cs" />
429428
<Compile Include="System\Reflection\Runtime\General\TypeForwardInfo.cs" />

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/RuntimeTypeHandleKey.cs

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeUnifier.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ private static void ValidateElementType(RuntimeTypeInfo elementType, bool multiD
222222
{
223223
Debug.Assert(multiDim || rank == 1);
224224

225-
if (elementType.IsByRef)
225+
if (elementType.IsByRef || elementType.IsVoid)
226226
throw new TypeLoadException(SR.Format(SR.ArgumentException_InvalidArrayElementType, elementType));
227227
}
228228
}

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ protected RuntimeTypeInfo()
5757

5858
public bool IsGenericType => IsGenericTypeDefinition || IsConstructedGenericType;
5959

60+
public bool IsVoid => InternalTypeHandleIfAvailable.Equals(typeof(void).TypeHandle);
61+
6062
public abstract string Name { get; }
6163

6264
public abstract Assembly Assembly { get; }

src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Validation.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,11 @@ private static TypeDesc EnsureLoadableTypeUncached(TypeDesc type)
283283
ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
284284
}
285285

286-
// It might seem reasonable to disallow array of void, but the CLR doesn't prevent that too hard.
287-
// E.g. "newarr void" will fail, but "newarr void[]" or "ldtoken void[]" will succeed.
286+
if (parameterType.IsVoid)
287+
{
288+
// Arrays of System.Void are not allowed
289+
ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
290+
}
288291
}
289292
}
290293
else if (type.IsFunctionPointer)

src/coreclr/vm/clsload.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2984,19 +2984,20 @@ TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracke
29842984
THROW_BAD_FORMAT_MAYBE((kind != ELEMENT_TYPE_SZARRAY) || rank == 1, BFA_SDARRAY_BADRANK, pLoaderModule);
29852985

29862986
// Arrays of BYREFS not allowed
2987-
if (paramType.GetInternalCorElementType() == ELEMENT_TYPE_BYREF)
2987+
if (paramType.IsByRef())
29882988
{
29892989
ThrowTypeLoadException(pKey, IDS_CLASSLOAD_BYREFARRAY);
29902990
}
29912991

29922992
// Arrays of ByRefLike types not allowed
2993-
MethodTable* pMT = paramType.GetMethodTable();
2994-
if (pMT != NULL)
2993+
if (paramType.IsByRefLike())
29952994
{
2996-
if (pMT->IsByRefLike())
2997-
{
2998-
ThrowTypeLoadException(pKey, IDS_CLASSLOAD_BYREFLIKEARRAY);
2999-
}
2995+
ThrowTypeLoadException(pKey, IDS_CLASSLOAD_BYREFLIKEARRAY);
2996+
}
2997+
2998+
if (paramType.GetSignatureCorElementType() == ELEMENT_TYPE_VOID)
2999+
{
3000+
ThrowTypeLoadException(pKey, IDS_CLASSLOAD_VOIDARRAY);
30003001
}
30013002

30023003
if (rank > MAX_RANK)

src/coreclr/vm/gchelpers.cpp

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -380,12 +380,6 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS
380380
_ASSERTE(pArrayMT->CheckInstanceActivated());
381381
_ASSERTE(pArrayMT->GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY);
382382

383-
CorElementType elemType = pArrayMT->GetArrayElementType();
384-
385-
// Disallow the creation of void[] (an array of System.Void)
386-
if (elemType == ELEMENT_TYPE_VOID)
387-
COMPlusThrow(kArgumentException);
388-
389383
if (cElements < 0)
390384
COMPlusThrow(kOverflowException);
391385

@@ -408,7 +402,7 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS
408402
#endif
409403

410404
#ifdef FEATURE_DOUBLE_ALIGNMENT_HINT
411-
if ((elemType == ELEMENT_TYPE_R8) &&
405+
if ((pArrayMT->GetArrayElementType() == ELEMENT_TYPE_R8) &&
412406
((DWORD)cElements >= g_pConfig->GetDoubleArrayToLargeObjectHeapThreshold()))
413407
{
414408
STRESS_LOG2(LF_GC, LL_INFO10, "Allocating double MD array of size %d and length %d to large object heap\n", totalSize, cElements);
@@ -431,7 +425,7 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS
431425
else
432426
{
433427
#ifndef FEATURE_64BIT_ALIGNMENT
434-
if ((DATA_ALIGNMENT < sizeof(double)) && (elemType == ELEMENT_TYPE_R8) &&
428+
if ((DATA_ALIGNMENT < sizeof(double)) && (pArrayMT->GetArrayElementType() == ELEMENT_TYPE_R8) &&
435429
(totalSize < g_pConfig->GetGCLOHThreshold() - MIN_OBJECT_SIZE))
436430
{
437431
// Creation of an array of doubles, not in the large object heap.
@@ -508,18 +502,12 @@ OBJECTREF TryAllocateFrozenSzArray(MethodTable* pArrayMT, INT32 cElements)
508502

509503
// The initial validation is copied from AllocateSzArray impl
510504

511-
CorElementType elemType = pArrayMT->GetArrayElementType();
512-
513505
if (pArrayMT->ContainsPointers() && cElements > 0)
514506
{
515507
// For arrays with GC pointers we can only work with empty arrays
516508
return NULL;
517509
}
518510

519-
// Disallow the creation of void[] (an array of System.Void)
520-
if (elemType == ELEMENT_TYPE_VOID)
521-
COMPlusThrow(kArgumentException);
522-
523511
if (cElements < 0)
524512
COMPlusThrow(kOverflowException);
525513

@@ -542,7 +530,7 @@ OBJECTREF TryAllocateFrozenSzArray(MethodTable* pArrayMT, INT32 cElements)
542530

543531
// FrozenObjectHeapManager doesn't yet support objects with a custom alignment,
544532
// so we give up on arrays of value types requiring 8 byte alignment on 32bit platforms.
545-
if ((DATA_ALIGNMENT < sizeof(double)) && (elemType == ELEMENT_TYPE_R8))
533+
if ((DATA_ALIGNMENT < sizeof(double)) && (pArrayMT->GetArrayElementType() == ELEMENT_TYPE_R8))
546534
{
547535
return NULL;
548536
}
@@ -637,11 +625,6 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs,
637625
CorElementType kind = pArrayMT->GetInternalCorElementType();
638626
_ASSERTE(kind == ELEMENT_TYPE_ARRAY || kind == ELEMENT_TYPE_SZARRAY);
639627

640-
CorElementType elemType = pArrayMT->GetArrayElementType();
641-
// Disallow the creation of void[,] (a multi-dim array of System.Void)
642-
if (elemType == ELEMENT_TYPE_VOID)
643-
COMPlusThrow(kArgumentException);
644-
645628
// Calculate the total number of elements in the array
646629
UINT32 cElements;
647630
bool maxArrayDimensionLengthOverflow = false;
@@ -715,7 +698,7 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs,
715698
#endif
716699

717700
#ifdef FEATURE_DOUBLE_ALIGNMENT_HINT
718-
if ((elemType == ELEMENT_TYPE_R8) &&
701+
if ((pArrayMT->GetArrayElementType() == ELEMENT_TYPE_R8) &&
719702
(cElements >= g_pConfig->GetDoubleArrayToLargeObjectHeapThreshold()))
720703
{
721704
STRESS_LOG2(LF_GC, LL_INFO10, "Allocating double MD array of size %d and length %d to large object heap\n", totalSize, cElements);

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ActivatorTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ public void CreateInstance_ContainsGenericParameters_ThrowsArgumentException(Typ
154154
public static IEnumerable<object[]> CreateInstance_InvalidType_TestData()
155155
{
156156
yield return new object[] { typeof(void) };
157-
yield return new object[] { typeof(void).MakeArrayType() };
158157
yield return new object[] { typeof(ArgIterator) };
159158
}
160159

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Type/TypeTests.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ static TypeTests()
4747
NonArrayBaseTypes = new List<Type>()
4848
{
4949
typeof(int),
50-
typeof(void),
5150
typeof(int*),
5251
typeof(Outside),
5352
typeof(Outside<int>),
@@ -244,15 +243,13 @@ public void MakeArrayType_Invoke_ReturnsExpected(Type t, Type tArrayExpected)
244243
public static IEnumerable<object[]> MakeArray_UnusualTypes_TestData()
245244
{
246245
yield return new object[] { typeof(StaticClass) };
247-
yield return new object[] { typeof(void) };
248246
yield return new object[] { typeof(GenericClass<>) };
249247
yield return new object[] { typeof(GenericClass<>).MakeGenericType(typeof(GenericClass<>)) };
250248
yield return new object[] { typeof(GenericClass<>).GetTypeInfo().GetGenericArguments()[0] };
251249
}
252250

253251
[Theory]
254252
[MemberData(nameof(MakeArray_UnusualTypes_TestData))]
255-
[ActiveIssue("https://github.com/dotnet/runtime/issues/52072", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
256253
public void MakeArrayType_UnusualTypes_ReturnsExpected(Type t)
257254
{
258255
Type tArray = t.MakeArrayType();
@@ -298,18 +295,19 @@ public void MakeArrayType_InvokeRankTwo_ReturnsExpected(Type t, Type tArrayExpec
298295
Assert.Equal(t.ToString() + "[,]", tArray.ToString());
299296
}
300297

301-
public static IEnumerable<object[]> MakeArrayType_ByRef_TestData()
298+
public static IEnumerable<object[]> MakeArrayType_InvalidElementType_TestData()
302299
{
303300
yield return new object[] { typeof(int).MakeByRefType() };
304301
yield return new object[] { typeof(TypedReference) };
305302
yield return new object[] { typeof(ArgIterator) };
306303
yield return new object[] { typeof(RuntimeArgumentHandle) };
307304
yield return new object[] { typeof(Span<int>) };
305+
yield return new object[] { typeof(void) };
308306
}
309307

310308
[Theory]
311-
[MemberData(nameof(MakeArrayType_ByRef_TestData))]
312-
public void MakeArrayType_ByRef_ThrowsTypeLoadException(Type t)
309+
[MemberData(nameof(MakeArrayType_InvalidElementType_TestData))]
310+
public void MakeArrayType_InvalidElementType_ThrowsTypeLoadException(Type t)
313311
{
314312
Assert.Throws<TypeLoadException>(() => t.MakeArrayType());
315313
}
@@ -607,7 +605,6 @@ public void IsSZArray_FalseForNonArrayTypes()
607605
}
608606

609607
[Fact]
610-
[ActiveIssue("https://github.com/dotnet/runtime/issues/52072", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
611608
public void IsSZArray_TrueForSZArrayTypes()
612609
{
613610
foreach (Type type in NonArrayBaseTypes.Select(nonArrayBaseType => nonArrayBaseType.MakeArrayType()))
@@ -659,7 +656,6 @@ public void IsVariableBoundArray_FalseForNonArrayTypes()
659656
}
660657

661658
[Fact]
662-
[ActiveIssue("https://github.com/dotnet/runtime/issues/52072", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
663659
public void IsVariableBoundArray_FalseForSZArrayTypes()
664660
{
665661
foreach (Type type in NonArrayBaseTypes.Select(nonArrayBaseType => nonArrayBaseType.MakeArrayType()))

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,12 +1170,8 @@ mono_class_create_bounded_array (MonoClass *eclass, guint32 rank, gboolean bound
11701170
klass->rank = GUINT32_TO_UINT8 (rank);
11711171
klass->element_class = eclass;
11721172

1173-
if (m_class_get_byval_arg (eclass)->type == MONO_TYPE_TYPEDBYREF) {
1174-
/*Arrays of those two types are invalid.*/
1175-
ERROR_DECL (prepared_error);
1176-
mono_error_set_invalid_program (prepared_error, "Arrays of System.TypedReference types are invalid.");
1177-
mono_class_set_failure (klass, mono_error_box (prepared_error, klass->image));
1178-
mono_error_cleanup (prepared_error);
1173+
if (MONO_TYPE_IS_VOID (m_class_get_byval_arg (eclass))) {
1174+
mono_class_set_type_load_failure (klass, "Arrays of System.Void types are invalid.");
11791175
} else if (m_class_is_byreflike (eclass)) {
11801176
/* .NET Core throws a type load exception: "Could not create array type 'fullname[]'" */
11811177
char *full_name = mono_type_get_full_name (eclass);

src/mono/mono/metadata/icall.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5966,9 +5966,9 @@ check_for_invalid_array_type (MonoType *type, MonoError *error)
59665966
gboolean allowed = TRUE;
59675967
char *name;
59685968

5969-
if (m_type_is_byref (type))
5969+
if (MONO_TYPE_IS_VOID (type))
59705970
allowed = FALSE;
5971-
else if (type->type == MONO_TYPE_TYPEDBYREF)
5971+
else if (m_type_is_byref (type))
59725972
allowed = FALSE;
59735973

59745974
MonoClass *klass = mono_class_from_mono_type_internal (type);

src/tests/JIT/Regression/CLR-x86-EJIT/v1-m10/b02353/b02353.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ public class Bug
4242
Type.GetType("System.Object"),
4343
Type.GetType("Simple"),
4444
Type.GetType("System.Empty[]"),
45-
Type.GetType("System.Void[]"),
4645
Type.GetType("System.Boolean[]"),
4746
Type.GetType("System.Char[]"),
4847
Type.GetType("System.SByte[]"),
@@ -66,7 +65,6 @@ public class Bug
6665
Type.GetType("System.Object[]"),
6766
Type.GetType("Simple[]"),
6867
Type.GetType("System.Empty[][]"),
69-
Type.GetType("System.Void[][]"),
7068
Type.GetType("System.Boolean[][]"),
7169
Type.GetType("System.Char[][]"),
7270
Type.GetType("System.SByte[][]"),
@@ -90,7 +88,6 @@ public class Bug
9088
Type.GetType("System.Object[][]"),
9189
Type.GetType("Simple[][]"),
9290
Type.GetType("System.Empty[][][]"),
93-
Type.GetType("System.Void[][][]"),
9491
Type.GetType("System.Boolean[][][]"),
9592
Type.GetType("System.Char[][][]"),
9693
Type.GetType("System.SByte[][][]"),

0 commit comments

Comments
 (0)