From 8c62f71d609e9b66a80234b1ae9b0bb98273d61f Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 8 Jul 2025 16:18:36 -0700 Subject: [PATCH 1/2] Add test case that crashes LibraryImportGenerator --- .../CodeSnippets.cs | 88 +++++++++++++++++++ .../CompileFails.cs | 3 + 2 files changed, 91 insertions(+) diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs index 56eb1d52dbe1ee..3228bfc44ed64f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs @@ -1438,5 +1438,93 @@ public Native(S s) public static string TypeUsage(string attr) => MarshalUsingParametersAndModifiers("S", "Marshaller", attr); } + + public const string ImproperCollectionWithMarshalUsingOnElements = """ + using System; + using System.Collections.Generic; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + + class MyList + { + } + + class NotMyList {} + + class Example + { + public int Value; + } + + internal static partial class PInvoke + { + [LibraryImport("NativeLibrary", EntryPoint = "ProcessExample")] + public static partial int ProcessExample([MarshalUsing(typeof(ListMarshaller<,>), CountElementName = nameof(exampleCount)), MarshalUsing(typeof(ExampleMarshaller), ElementIndirectionDepth = 1)] MyList examples, ref int exampleCount); + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(MyList<>), MarshalMode.Default, typeof(ListMarshaller<,>.Marshaller))] + public unsafe static class ListMarshaller where TUnmanagedElement : unmanaged + { + public static class Marshaller + { + public static byte* AllocateContainerForUnmanagedElements(NotMyList managed, out int numElements) + { + numElements = default; + return default; + } + + public static ReadOnlySpan GetManagedValuesSource(NotMyList managed) + => default; + + public static Span GetUnmanagedValuesDestination(byte* unmanaged, int numElements) + => new Span((TUnmanagedElement*)unmanaged, numElements); + + public static List AllocateContainerForManagedElements(byte* unmanaged, int length) + => new List(length); + + public static Span GetManagedValuesDestination(NotMyList managed) + => default; + + public static ReadOnlySpan GetUnmanagedValuesSource(byte* nativeValue, int numElements) + => new ReadOnlySpan((TUnmanagedElement*)nativeValue, numElements); + + public static void Free(byte* unmanaged) + => NativeMemory.Free(unmanaged); + } + } + + [CustomMarshaller(typeof(Example), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))] + [CustomMarshaller(typeof(Example), MarshalMode.ManagedToUnmanagedOut, typeof(ManagedToNativeOutFinally))] + static class ExampleMarshaller + { + public static class ManagedToNativeOutFinally + { + public static Example ConvertToManagedFinally(int managed) + { + return default; + } + + public static void Free(int unmanaged) + { + } + } + + public struct ManagedToNative + { + Example managed; + + public static int BufferSize => sizeof(int); + + public void FromManaged(Example managed, Span buffer) => this.managed = managed; + + public int ToUnmanaged() => managed.Value; + + public void OnInvoked() { } + + public void Free() { } + } + } + """; } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs index 605cd6b80386c5..da02a0518f0467 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs @@ -57,6 +57,9 @@ public static IEnumerable CodeSnippetsToCompile() // Not LibraryImportAttribute yield return new object[] { ID(), CodeSnippets.UserDefinedPrefixedAttributes, Array.Empty() }; + // Bug: crashes + // yield return new[] { ID(), CodeSnippets.ImproperCollectionWithMarshalUsingOnElements }; + // No explicit marshalling for char or string yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(), new[] { From 7f916d92ed04e16d5d57dcd5092828bd81ac53ba Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 8 Jul 2025 16:37:00 -0700 Subject: [PATCH 2/2] Update src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs Co-authored-by: Aaron Robinson --- .../tests/LibraryImportGenerator.UnitTests/CompileFails.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs index da02a0518f0467..952f8ef2890c81 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs @@ -57,7 +57,7 @@ public static IEnumerable CodeSnippetsToCompile() // Not LibraryImportAttribute yield return new object[] { ID(), CodeSnippets.UserDefinedPrefixedAttributes, Array.Empty() }; - // Bug: crashes + // Bug: https://github.com/dotnet/runtime/issues/117448 // yield return new[] { ID(), CodeSnippets.ImproperCollectionWithMarshalUsingOnElements }; // No explicit marshalling for char or string