Fix Mono AOT crash on struct fields with unsupported marshal conversion#125575
Open
lewing wants to merge 1 commit intodotnet:mainfrom
Open
Fix Mono AOT crash on struct fields with unsupported marshal conversion#125575lewing wants to merge 1 commit intodotnet:mainfrom
lewing wants to merge 1 commit intodotnet:mainfrom
Conversation
When a struct contains a reference type field (string, class, array) with MONO_MARSHAL_CONV_NONE (e.g. from UnmanagedType.CustomMarshaler), the AOT compiler hits g_assert_not_reached() in emit_struct_conv_full, causing a SIGABRT. This happens with StructWithCustomMarshalerField in the ICustomMarshaler test: a struct with [MarshalAs(UnmanagedType.CustomMarshaler)] on a string field. The test expects a TypeLoadException at runtime, but the AOT compiler crashes before it gets there. Handle MONO_TYPE_STRING, MONO_TYPE_CLASS, MONO_TYPE_OBJECT, MONO_TYPE_SZARRAY, and MONO_TYPE_ARRAY in the MONO_MARSHAL_CONV_NONE path by emitting a MarshalDirectiveException instead of asserting. This lets the AOT compiler complete and the runtime throw the expected exception. Fixes dotnet#125168 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a Mono AOT compiler crash when encountering struct fields whose marshaling resolves to MONO_MARSHAL_CONV_NONE for reference types (e.g., string with UnmanagedType.CustomMarshaler), by emitting a MarshalDirectiveException path instead of asserting.
Changes:
- Extends the
MONO_MARSHAL_CONV_NONEhandling inmono_marshal_shared_emit_struct_conv_fullto cover additional reference-type field kinds (STRING,CLASS, arrays) alongsideOBJECT. - Replaces the previous special-case
MONO_TYPE_OBJECTmessage with a consolidated, more descriptive error message for these reference types.
You can also share your feedback on Copilot code review. Take the survey.
Comment on lines
+911
to
+913
| char *msg = g_strdup_printf ("Type %s with field type %s cannot be marshaled as an unmanaged struct field.", | ||
| mono_type_full_name (m_class_get_byval_arg (klass)), | ||
| mono_type_full_name (ftype)); |
Member
Author
There was a problem hiding this comment.
Good catch — this is a real leak. However, all existing mono_type_full_name() call sites in this file have the same pattern (lines 778, 816, 866). Filed #125576 to track fixing all of them together rather than making this PR inconsistent with the rest of the file.
This was referenced Mar 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The Mono AOT compiler crashes with
SIGABRTwhen compiling assemblies that contain structs with reference-type fields (string, class, array) that resolve toMONO_MARSHAL_CONV_NONE— for example a string field with[MarshalAs(UnmanagedType.CustomMarshaler)].This hits the
linux-x64 Release AllSubsets_Mono_LLVMFULLAOT_RuntimeIntrinsicsTestsleg on every build (#125168, 22 hits/month). The failing assembly isICustomMarshaler_TargetUnix.dllwhich contains:The test that uses this struct (
CustomMarshaler_StructWithCustomMarshalerFieldIsMarshaledCorrectly) expects aTypeLoadExceptionat runtime — the struct is intentionally invalid. But the AOT compiler crashes before the test can run.Root cause
In
mono_marshal_shared_emit_struct_conv_full(marshal-shared.c):mono_type_to_unmanagedseesMONO_TYPE_STRING+MONO_NATIVE_CUSTOM→ returnsMONO_NATIVE_CUSTOMwithconv = MONO_MARSHAL_CONV_NONEMONO_MARSHAL_CONV_NONEswitch enters the inner type switchMONO_TYPE_STRING(0x0e) is not handled → falls to default →g_assert_not_reached()→ SIGABRTFix
Handle
MONO_TYPE_STRING,MONO_TYPE_CLASS,MONO_TYPE_OBJECT,MONO_TYPE_SZARRAY, andMONO_TYPE_ARRAYin theMONO_MARSHAL_CONV_NONEpath by emitting aMarshalDirectiveExceptioninstead of asserting. This lets the AOT compiler complete successfully and the runtime throw the expected exception when the type is actually used.The existing
MONO_TYPE_OBJECTcase already did this (with a less descriptive "COM support was disabled" message). This PR consolidates all reference types into the same error path with a more accurate message.Fixes #125168