-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Fix TypeLoadException in GetMarshalAs when SafeArray has zero-length user-defined type name #124408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,13 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Licensed to the .NET Foundation under one or more agreements. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The .NET Foundation licenses this file to you under the MIT license. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.IO; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Reflection; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Reflection.Emit; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Reflection.Metadata; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Reflection.Metadata.Ecma335; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Reflection.PortableExecutable; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Runtime.Loader; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Xunit; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| namespace System.Runtime.InteropServices.Tests | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -11,7 +18,7 @@ public class MarshalAsAttributeTests | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [InlineData((UnmanagedType)(-1))] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [InlineData(UnmanagedType.HString)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [InlineData((UnmanagedType)int.MaxValue)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void Ctor_UmanagedTye(UnmanagedType unmanagedType) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void Ctor_UnmanagedType(UnmanagedType unmanagedType) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var attribute = new MarshalAsAttribute(unmanagedType); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Assert.Equal(unmanagedType, attribute.Value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -26,5 +33,86 @@ public void Ctor_ShortUnmanagedType(short umanagedType) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var attribute = new MarshalAsAttribute(umanagedType); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Assert.Equal((UnmanagedType)umanagedType, attribute.Value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Fact] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void SafeArrayParameter_ZeroLengthUserDefinedSubType_DoesNotThrow() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+37
to
+39
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| byte[] peBytes = BuildPEWithSafeArrayMarshalBlob(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AssemblyLoadContext alc = new(nameof(SafeArrayParameter_ZeroLengthUserDefinedSubType_DoesNotThrow), isCollectible: true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Assembly asm = alc.LoadFromStream(new MemoryStream(peBytes)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Type iface = asm.GetType("TestInterface"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MethodInfo method = iface.GetMethod("TestMethod"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ParameterInfo param = method.GetParameters()[0]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Must not throw TypeLoadException. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _ = param.GetCustomAttributes(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MarshalAsAttribute attr = (MarshalAsAttribute)Attribute.GetCustomAttribute(param, typeof(MarshalAsAttribute)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+46
to
+53
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Type iface = asm.GetType("TestInterface"); | |
| MethodInfo method = iface.GetMethod("TestMethod"); | |
| ParameterInfo param = method.GetParameters()[0]; | |
| // Must not throw TypeLoadException. | |
| _ = param.GetCustomAttributes(false); | |
| MarshalAsAttribute attr = (MarshalAsAttribute)Attribute.GetCustomAttribute(param, typeof(MarshalAsAttribute)); | |
| Type iface = asm.GetType("TestInterface") ?? throw new InvalidOperationException("Test interface 'TestInterface' not found in generated assembly."); | |
| MethodInfo method = iface.GetMethod("TestMethod") ?? throw new InvalidOperationException("Test method 'TestMethod' not found on 'TestInterface'."); | |
| ParameterInfo param = method.GetParameters()[0]; | |
| // Must not throw TypeLoadException. | |
| _ = param.GetCustomAttributes(false); | |
| MarshalAsAttribute attr = (MarshalAsAttribute?)Attribute.GetCustomAttribute(param, typeof(MarshalAsAttribute)) | |
| ?? throw new InvalidOperationException("Expected MarshalAsAttribute not found on parameter."); |
Copilot
AI
Feb 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MemoryStream passed to AssemblyLoadContext.LoadFromStream isn’t disposed. Please wrap it in a using/try-finally so the stream is closed promptly (this can also help collectible ALC unloading behavior).
| Assembly asm = alc.LoadFromStream(new MemoryStream(peBytes)); | |
| Type iface = asm.GetType("TestInterface"); | |
| MethodInfo method = iface.GetMethod("TestMethod"); | |
| ParameterInfo param = method.GetParameters()[0]; | |
| // Must not throw TypeLoadException. | |
| _ = param.GetCustomAttributes(false); | |
| MarshalAsAttribute attr = (MarshalAsAttribute)Attribute.GetCustomAttribute(param, typeof(MarshalAsAttribute)); | |
| Assert.NotNull(attr); | |
| Assert.Equal(UnmanagedType.SafeArray, attr.Value); | |
| Assert.Null(attr.SafeArrayUserDefinedSubType); | |
| using (MemoryStream peStream = new(peBytes)) | |
| { | |
| Assembly asm = alc.LoadFromStream(peStream); | |
| Type iface = asm.GetType("TestInterface"); | |
| MethodInfo method = iface.GetMethod("TestMethod"); | |
| ParameterInfo param = method.GetParameters()[0]; | |
| // Must not throw TypeLoadException. | |
| _ = param.GetCustomAttributes(false); | |
| MarshalAsAttribute attr = (MarshalAsAttribute)Attribute.GetCustomAttribute(param, typeof(MarshalAsAttribute)); | |
| Assert.NotNull(attr); | |
| Assert.Equal(UnmanagedType.SafeArray, attr.Value); | |
| Assert.Null(attr.SafeArrayUserDefinedSubType); | |
| } |
Copilot
AI
Feb 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid hardcoding ParameterHandle(1) here; the row id depends on how the Params table is emitted and could change (e.g., if a return parameter row gets introduced). Capture the ParameterBuilder returned from DefineParameter and derive the correct ParameterHandle from its metadata token when calling AddMarshallingDescriptor, so the test is resilient to metadata layout changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This catch-block comment says "we return the bad type name", but MarshalAsAttribute doesn’t expose the SafeArray user-defined subtype name string anywhere (only the resolved Type via SafeArrayUserDefinedSubType). Please adjust the comment to reflect the actual behavior (e.g., that we swallow TypeLoadException and leave SafeArrayUserDefinedSubType unset) to avoid misleading future maintainers.