From 0682f43d3adbac0968ec1f8ee41ef082de96f188 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Jul 2026 23:24:25 +0000 Subject: [PATCH 01/16] Initial plan From 8f419ea69123d9bc8888591ec3953fd9e33e007c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Jul 2026 23:55:34 +0000 Subject: [PATCH 02/16] Handle AssociatedSourceType on UnmanagedCallersOnly Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/coreclr/vm/callconvbuilder.cpp | 5 ++++- .../UnmanagedCallersOnlyBasicTest.cs | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/callconvbuilder.cpp b/src/coreclr/vm/callconvbuilder.cpp index 60d5d5eb0bbe71..a42b3b908a07f7 100644 --- a/src/coreclr/vm/callconvbuilder.cpp +++ b/src/coreclr/vm/callconvbuilder.cpp @@ -550,7 +550,7 @@ bool CallConv::TryGetCallingConventionFromUnmanagedCallersOnly(_In_ MethodDesc* // UnmanagedCallersOnly each // have optional named arguments. - CaNamedArg namedArgs[2]; + CaNamedArg namedArgs[3]; // For the UnmanagedCallersOnly scenario. CaType caCallConvs; @@ -563,6 +563,9 @@ bool CallConv::TryGetCallingConventionFromUnmanagedCallersOnly(_In_ MethodDesc* CaTypeCtor caEntryPoint(SERIALIZATION_TYPE_STRING); namedArgs[1].Init("EntryPoint", SERIALIZATION_TYPE_STRING, caEntryPoint); + CaTypeCtor caAssociatedSourceType(SERIALIZATION_TYPE_TYPE); + namedArgs[2].Init("AssociatedSourceType", SERIALIZATION_TYPE_TYPE, caAssociatedSourceType); + InlineFactory, 4> caValueArrayFactory; Assembly* assembly = pMD->GetLoaderModule()->GetAssembly(); IfFailThrow(CustomAttribute::ParseArgumentValues( diff --git a/src/tests/Interop/UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs b/src/tests/Interop/UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs index ffbafe2bc1669e..ed6b89cd067887 100644 --- a/src/tests/Interop/UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs +++ b/src/tests/Interop/UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs @@ -45,6 +45,12 @@ public static int ManagedDoubleCallback(int n) return DoubleImpl(n); } + [UnmanagedCallersOnly(AssociatedSourceType = typeof(UnmanagedCallersOnlyBasicTest))] + public static int ManagedDoubleCallback_AssociatedSourceType(int n) + { + return DoubleImpl(n); + } + [ActiveIssue("https://github.com/dotnet/runtime/issues/64127", typeof(PlatformDetection), nameof(PlatformDetection.PlatformDoesNotSupportNativeTestAssets))] [ActiveIssue("Needs coreclr build", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoFULLAOT))] [ActiveIssue("needs triage", TestPlatforms.Android)] @@ -60,7 +66,17 @@ public static void TestUnmanagedCallersOnlyValid() Assert.Equal(expected, UnmanagedCallersOnlyDll.CallManagedProc((IntPtr)(delegate* unmanaged)&ManagedDoubleCallback, n)); } - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])] + [Fact] + public static void TestUnmanagedCallersOnlyValid_AssociatedSourceType() + { + Console.WriteLine($"Running {nameof(TestUnmanagedCallersOnlyValid_AssociatedSourceType)}..."); + + int n = 12345; + int expected = DoubleImpl(n); + Assert.Equal(expected, ((delegate* unmanaged)&ManagedDoubleCallback_AssociatedSourceType)(n)); + } + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])] public static int ManagedDoubleCallback_Stdcall(int n) { return DoubleImpl(n); From 09664ad092d854590adc0d69c14a58cc8241e8ac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 00:01:08 +0000 Subject: [PATCH 03/16] Align UnmanagedCallersOnly test platform gating Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/Interop/UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs b/src/tests/Interop/UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs index ed6b89cd067887..f80c6901e9e924 100644 --- a/src/tests/Interop/UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs +++ b/src/tests/Interop/UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs @@ -66,6 +66,9 @@ public static void TestUnmanagedCallersOnlyValid() Assert.Equal(expected, UnmanagedCallersOnlyDll.CallManagedProc((IntPtr)(delegate* unmanaged)&ManagedDoubleCallback, n)); } + [ActiveIssue("Needs coreclr build", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoFULLAOT))] + [ActiveIssue("needs triage", TestPlatforms.Android)] + [ActiveIssue("needs triage", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] [Fact] public static void TestUnmanagedCallersOnlyValid_AssociatedSourceType() { From 5cd28fc0225e2491ab16b9e070e4addb418fa6bb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 00:02:55 +0000 Subject: [PATCH 04/16] Match native-asset gating for UCO regression test Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tests/Interop/UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs b/src/tests/Interop/UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs index f80c6901e9e924..eaf36d4dfd3125 100644 --- a/src/tests/Interop/UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs +++ b/src/tests/Interop/UnmanagedCallersOnlyBasic/UnmanagedCallersOnlyBasicTest.cs @@ -66,6 +66,7 @@ public static void TestUnmanagedCallersOnlyValid() Assert.Equal(expected, UnmanagedCallersOnlyDll.CallManagedProc((IntPtr)(delegate* unmanaged)&ManagedDoubleCallback, n)); } + [ActiveIssue("https://github.com/dotnet/runtime/issues/64127", typeof(PlatformDetection), nameof(PlatformDetection.PlatformDoesNotSupportNativeTestAssets))] [ActiveIssue("Needs coreclr build", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoFULLAOT))] [ActiveIssue("needs triage", TestPlatforms.Android)] [ActiveIssue("needs triage", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] From 7bde47b2ff55a5aa76b34c298d4b2fe5e4c06d4d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 01:43:22 +0000 Subject: [PATCH 05/16] Handle UCO AssociatedSourceType on Mono Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/mono/mono/metadata/marshal.c | 263 +++++++++++++++++++++++++++++-- 1 file changed, 248 insertions(+), 15 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 39d522bb570c6f..31dd5d209e74ad 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3450,6 +3450,252 @@ mono_marshal_set_signature_callconv_from_attribute(MonoMethodSignature *sig, Mon // - Adjust the code above with 'anding' the attribute parameter value } +#define MONO_MARSHAL_CATTR_TYPE_SYSTEM_TYPE 0x50 +#define MONO_MARSHAL_CATTR_TYPE_FIELD 0x53 + +static guint16 +mono_marshal_read16 (const char *p) +{ + return (guint16)((guint8)p [0] | ((guint8)p [1] << 8)); +} + +static guint32 +mono_marshal_read32 (const char *p) +{ + return (guint32)((guint8)p [0] | ((guint8)p [1] << 8) | ((guint8)p [2] << 16) | ((guint8)p [3] << 24)); +} + +static gboolean +mono_marshal_check_blob (const char *p, guint32 len, const char *end) +{ + return p <= end && (gsize)(end - p) >= len; +} + +static gboolean +mono_marshal_decode_blob_size (const char **p, const char *end, guint32 *size) +{ + if (!mono_marshal_check_blob (*p, 1, end)) + return FALSE; + + const guint8 first = (guint8)**p; + if ((first & 0x80) == 0) { + *size = first; + *p += 1; + return TRUE; + } + + if ((first & 0xC0) == 0x80) { + if (!mono_marshal_check_blob (*p, 2, end)) + return FALSE; + + *size = ((guint32)(first & 0x3F) << 8) | (guint8)(*p) [1]; + *p += 2; + return TRUE; + } + + if ((first & 0xE0) == 0xC0) { + if (!mono_marshal_check_blob (*p, 4, end)) + return FALSE; + + *size = ((guint32)(first & 0x1F) << 24) | ((guint32)(guint8)(*p) [1] << 16) | ((guint32)(guint8)(*p) [2] << 8) | (guint8)(*p) [3]; + *p += 4; + return TRUE; + } + + return FALSE; +} + +static gboolean +mono_marshal_skip_ser_string (const char **p, const char *end) +{ + if (!mono_marshal_check_blob (*p, 1, end)) + return FALSE; + +MONO_DISABLE_WARNING (4310) // cast truncates constant value + if (**p == (char)0xFF) { + *p += 1; + return TRUE; + } +MONO_RESTORE_WARNING + + guint32 len; + if (!mono_marshal_decode_blob_size (p, end, &len)) + return FALSE; + if (!mono_marshal_check_blob (*p, len, end)) + return FALSE; + + *p += len; + return TRUE; +} + +static gboolean +mono_marshal_skip_cattr_element_value (const char **p, const char *end, guint8 type) +{ + switch (type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + if (!mono_marshal_check_blob (*p, 1, end)) + return FALSE; + *p += 1; + return TRUE; + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + if (!mono_marshal_check_blob (*p, 2, end)) + return FALSE; + *p += 2; + return TRUE; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_R4: + if (!mono_marshal_check_blob (*p, 4, end)) + return FALSE; + *p += 4; + return TRUE; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R8: + if (!mono_marshal_check_blob (*p, 8, end)) + return FALSE; + *p += 8; + return TRUE; + case MONO_TYPE_STRING: + case MONO_MARSHAL_CATTR_TYPE_SYSTEM_TYPE: + return mono_marshal_skip_ser_string (p, end); + default: + return FALSE; + } +} + +static gboolean +mono_marshal_skip_cattr_named_arg_value (const char **p, const char *end, guint8 type, guint8 array_type) +{ + if (type != MONO_TYPE_SZARRAY) + return mono_marshal_skip_cattr_element_value (p, end, type); + + if (!mono_marshal_check_blob (*p, 4, end)) + return FALSE; + + guint32 len = mono_marshal_read32 (*p); + *p += 4; + if (len == 0xFFFFFFFF) + return TRUE; + + for (guint32 i = 0; i < len; ++i) { + if (!mono_marshal_skip_cattr_element_value (p, end, array_type)) + return FALSE; + } + + return TRUE; +} + +static gboolean +mono_marshal_set_callconv_from_unmanaged_callers_only_attribute_data (MonoCustomAttrEntry *attr, MonoMethod *method, MonoMethodSignature *csig, MonoError *error) +{ + error_init (error); + + const char *p = (const char*)attr->data; + const char *end = p + attr->data_size; + + if (!mono_marshal_check_blob (p, 2, end) || mono_marshal_read16 (p) != 0x0001) + return FALSE; + p += 2; + + MonoMethodSignature *sig = mono_method_signature_internal (attr->ctor); + if (sig->param_count != 0) + return FALSE; + + if (!mono_marshal_check_blob (p, 2, end)) + return FALSE; + + guint16 num_named_args = mono_marshal_read16 (p); + p += 2; + + for (guint16 i = 0; i < num_named_args; ++i) { + if (!mono_marshal_check_blob (p, 2, end)) + return FALSE; + + guint8 named_type = (guint8)*p++; + guint8 data_type = (guint8)*p++; + guint8 array_type = 0; + + if (data_type == MONO_TYPE_SZARRAY) { + if (!mono_marshal_check_blob (p, 1, end)) + return FALSE; + array_type = (guint8)*p++; + } + + if ((data_type == MONO_TYPE_ENUM || (data_type == MONO_TYPE_SZARRAY && array_type == MONO_TYPE_ENUM)) && !mono_marshal_skip_ser_string (&p, end)) + return FALSE; + + guint32 name_len; + if (!mono_marshal_decode_blob_size (&p, end, &name_len)) + return FALSE; + if (!mono_marshal_check_blob (p, name_len, end)) + return FALSE; + + const char *name = p; + p += name_len; + + if (name_len != strlen ("CallConvs") || strncmp (name, "CallConvs", name_len) != 0) { + if (!mono_marshal_skip_cattr_named_arg_value (&p, end, data_type, array_type)) + return FALSE; + continue; + } + + if (named_type != MONO_MARSHAL_CATTR_TYPE_FIELD || data_type != MONO_TYPE_SZARRAY || array_type != MONO_MARSHAL_CATTR_TYPE_SYSTEM_TYPE) + return FALSE; + if (!mono_marshal_check_blob (p, 4, end)) + return FALSE; + + guint32 callconv_count = mono_marshal_read32 (p); + p += 4; + if (callconv_count == 0xFFFFFFFF) + continue; + + if (callconv_count > 1) + g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute parameter CallConvs, specified for method %s. Only the first calling convention will be taken into account", method->name); + + for (guint32 callconv_index = 0; callconv_index < callconv_count; ++callconv_index) { + if (!mono_marshal_check_blob (p, 1, end)) + return FALSE; + +MONO_DISABLE_WARNING (4310) // cast truncates constant value + if (*p == (char)0xFF) { + p += 1; + continue; + } +MONO_RESTORE_WARNING + + guint32 type_name_len; + if (!mono_marshal_decode_blob_size (&p, end, &type_name_len)) + return FALSE; + if (!mono_marshal_check_blob (p, type_name_len, end)) + return FALSE; + + if (callconv_index == 0) { + char *type_name = g_strndup (p, type_name_len); + MonoAssemblyLoadContext *alc = mono_image_get_alc (mono_defaults.corlib); + MonoType *callconv_type = mono_reflection_type_from_name_checked (type_name, alc, mono_defaults.corlib, error); + g_free (type_name); + return_val_if_nok (error, FALSE); + + if (callconv_type) + mono_marshal_set_signature_callconv_from_attribute (csig, callconv_type, error); + return_val_if_nok (error, FALSE); + } + + p += type_name_len; + } + } + + return TRUE; +} + +#undef MONO_MARSHAL_CATTR_TYPE_FIELD +#undef MONO_MARSHAL_CATTR_TYPE_SYSTEM_TYPE + static void mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *method, MonoMethodSignature *csig) { @@ -3474,22 +3720,9 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met } if (attr != NULL) { - MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, error); + if (!mono_marshal_set_callconv_from_unmanaged_callers_only_attribute_data (attr, method, csig, error) && is_ok (error)) + mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Binary format of the specified custom attribute was invalid."); mono_error_assert_ok (error); - for (int i = 0; i < decoded_args->named_args_num; ++i) { - if (decoded_args->named_args_info [i].field && !strcmp (decoded_args->named_args_info [i].field->name, "CallConvs")) { - g_assertf(decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "UnmanagedCallersOnlyAttribute parameter %s must be an array, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); - MonoCustomAttrValueArray *calling_conventions = decoded_args->named_args[i]->value.array; - if (calling_conventions->len > 0) { - if (calling_conventions->len > 1) - g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute parameter %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name); - // TODO: Support multiple conventions? - MonoType* calling_convention = (MonoType*)calling_conventions->values[0].value.primitive; - mono_marshal_set_signature_callconv_from_attribute (csig, calling_convention, error); - } - } - } - mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); } if (!cinfo->cached) From 37e772ddf80cc53fab1c9ad90d2e6b8c1935556b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 01:45:45 +0000 Subject: [PATCH 06/16] Polish Mono UCO parser Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/mono/mono/metadata/marshal.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 31dd5d209e74ad..43245396bf0e71 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3452,6 +3452,8 @@ mono_marshal_set_signature_callconv_from_attribute(MonoMethodSignature *sig, Mon #define MONO_MARSHAL_CATTR_TYPE_SYSTEM_TYPE 0x50 #define MONO_MARSHAL_CATTR_TYPE_FIELD 0x53 +#define MONO_MARSHAL_CALLCONVS_NAME "CallConvs" +#define MONO_MARSHAL_CALLCONVS_NAME_LEN (sizeof (MONO_MARSHAL_CALLCONVS_NAME) - 1) static guint16 mono_marshal_read16 (const char *p) @@ -3488,7 +3490,7 @@ mono_marshal_decode_blob_size (const char **p, const char *end, guint32 *size) if (!mono_marshal_check_blob (*p, 2, end)) return FALSE; - *size = ((guint32)(first & 0x3F) << 8) | (guint8)(*p) [1]; + *size = ((guint32)(first & 0x3F) << 8) | (guint8)(*p)[1]; *p += 2; return TRUE; } @@ -3638,7 +3640,7 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute_data (MonoCustom const char *name = p; p += name_len; - if (name_len != strlen ("CallConvs") || strncmp (name, "CallConvs", name_len) != 0) { + if (name_len != MONO_MARSHAL_CALLCONVS_NAME_LEN || strncmp (name, MONO_MARSHAL_CALLCONVS_NAME, name_len) != 0) { if (!mono_marshal_skip_cattr_named_arg_value (&p, end, data_type, array_type)) return FALSE; continue; @@ -3655,7 +3657,7 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute_data (MonoCustom continue; if (callconv_count > 1) - g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute parameter CallConvs, specified for method %s. Only the first calling convention will be taken into account", method->name); + g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute field CallConvs, specified for method %s. Only the first calling convention will be taken into account", method->name); for (guint32 callconv_index = 0; callconv_index < callconv_count; ++callconv_index) { if (!mono_marshal_check_blob (p, 1, end)) @@ -3695,6 +3697,8 @@ MONO_RESTORE_WARNING #undef MONO_MARSHAL_CATTR_TYPE_FIELD #undef MONO_MARSHAL_CATTR_TYPE_SYSTEM_TYPE +#undef MONO_MARSHAL_CALLCONVS_NAME +#undef MONO_MARSHAL_CALLCONVS_NAME_LEN static void mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *method, MonoMethodSignature *csig) From baec198a3ea2e50c2fc3d4cb5c35a5b0d5ca24bd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 01:48:09 +0000 Subject: [PATCH 07/16] Address Mono UCO parser review Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/mono/mono/metadata/marshal.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 43245396bf0e71..b76d78e0af1bde 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3499,7 +3499,7 @@ mono_marshal_decode_blob_size (const char **p, const char *end, guint32 *size) if (!mono_marshal_check_blob (*p, 4, end)) return FALSE; - *size = ((guint32)(first & 0x1F) << 24) | ((guint32)(guint8)(*p) [1] << 16) | ((guint32)(guint8)(*p) [2] << 8) | (guint8)(*p) [3]; + *size = ((guint32)(first & 0x1F) << 24) | ((guint32)(guint8)(*p)[1] << 16) | ((guint32)(guint8)(*p)[2] << 8) | (guint8)(*p)[3]; *p += 4; return TRUE; } @@ -3657,7 +3657,7 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute_data (MonoCustom continue; if (callconv_count > 1) - g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute field CallConvs, specified for method %s. Only the first calling convention will be taken into account", method->name); + g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute field %s, specified for method %s. Only the first calling convention will be taken into account", MONO_MARSHAL_CALLCONVS_NAME, method->name); for (guint32 callconv_index = 0; callconv_index < callconv_count; ++callconv_index) { if (!mono_marshal_check_blob (p, 1, end)) @@ -3725,7 +3725,7 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met if (attr != NULL) { if (!mono_marshal_set_callconv_from_unmanaged_callers_only_attribute_data (attr, method, csig, error) && is_ok (error)) - mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Binary format of the specified custom attribute was invalid."); + mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Binary format of UnmanagedCallersOnlyAttribute on method %s was invalid.", method->name); mono_error_assert_ok (error); } From 8c14c5e0bb0de9b85c2e88e11cd6039f01c9245d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 04:25:20 +0000 Subject: [PATCH 08/16] Minimize Mono UCO AssociatedSourceType handling Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/mono/mono/metadata/marshal.c | 282 ++++++------------------------- 1 file changed, 48 insertions(+), 234 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index b76d78e0af1bde..d2b878bd4f08a4 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3450,256 +3450,55 @@ mono_marshal_set_signature_callconv_from_attribute(MonoMethodSignature *sig, Mon // - Adjust the code above with 'anding' the attribute parameter value } -#define MONO_MARSHAL_CATTR_TYPE_SYSTEM_TYPE 0x50 -#define MONO_MARSHAL_CATTR_TYPE_FIELD 0x53 -#define MONO_MARSHAL_CALLCONVS_NAME "CallConvs" -#define MONO_MARSHAL_CALLCONVS_NAME_LEN (sizeof (MONO_MARSHAL_CALLCONVS_NAME) - 1) - -static guint16 -mono_marshal_read16 (const char *p) -{ - return (guint16)((guint8)p [0] | ((guint8)p [1] << 8)); -} - -static guint32 -mono_marshal_read32 (const char *p) -{ - return (guint32)((guint8)p [0] | ((guint8)p [1] << 8) | ((guint8)p [2] << 16) | ((guint8)p [3] << 24)); -} - static gboolean -mono_marshal_check_blob (const char *p, guint32 len, const char *end) +mono_marshal_unmanaged_callers_only_has_only_associated_source_type (MonoCustomAttrEntry *attr) { - return p <= end && (gsize)(end - p) >= len; -} + const guint8 *data = attr->data; + const guint32 associated_source_type_name_len = sizeof ("AssociatedSourceType") - 1; + const guint32 minimum_data_size = 2 + 2 + 1 + 1 + 1 + associated_source_type_name_len; + const char *value; + const char *end; + guint32 value_len; -static gboolean -mono_marshal_decode_blob_size (const char **p, const char *end, guint32 *size) -{ - if (!mono_marshal_check_blob (*p, 1, end)) + if (attr->data_size < minimum_data_size) return FALSE; - - const guint8 first = (guint8)**p; - if ((first & 0x80) == 0) { - *size = first; - *p += 1; - return TRUE; - } - - if ((first & 0xC0) == 0x80) { - if (!mono_marshal_check_blob (*p, 2, end)) - return FALSE; - - *size = ((guint32)(first & 0x3F) << 8) | (guint8)(*p)[1]; - *p += 2; - return TRUE; - } - - if ((first & 0xE0) == 0xC0) { - if (!mono_marshal_check_blob (*p, 4, end)) - return FALSE; - - *size = ((guint32)(first & 0x1F) << 24) | ((guint32)(guint8)(*p)[1] << 16) | ((guint32)(guint8)(*p)[2] << 8) | (guint8)(*p)[3]; - *p += 4; - return TRUE; - } - - return FALSE; -} - -static gboolean -mono_marshal_skip_ser_string (const char **p, const char *end) -{ - if (!mono_marshal_check_blob (*p, 1, end)) + if (data [0] != 0x01 || data [1] != 0x00) return FALSE; - -MONO_DISABLE_WARNING (4310) // cast truncates constant value - if (**p == (char)0xFF) { - *p += 1; - return TRUE; - } -MONO_RESTORE_WARNING - - guint32 len; - if (!mono_marshal_decode_blob_size (p, end, &len)) - return FALSE; - if (!mono_marshal_check_blob (*p, len, end)) - return FALSE; - - *p += len; - return TRUE; -} - -static gboolean -mono_marshal_skip_cattr_element_value (const char **p, const char *end, guint8 type) -{ - switch (type) { - case MONO_TYPE_BOOLEAN: - case MONO_TYPE_I1: - case MONO_TYPE_U1: - if (!mono_marshal_check_blob (*p, 1, end)) - return FALSE; - *p += 1; - return TRUE; - case MONO_TYPE_CHAR: - case MONO_TYPE_I2: - case MONO_TYPE_U2: - if (!mono_marshal_check_blob (*p, 2, end)) - return FALSE; - *p += 2; - return TRUE; - case MONO_TYPE_I4: - case MONO_TYPE_U4: - case MONO_TYPE_R4: - if (!mono_marshal_check_blob (*p, 4, end)) - return FALSE; - *p += 4; - return TRUE; - case MONO_TYPE_I8: - case MONO_TYPE_U8: - case MONO_TYPE_R8: - if (!mono_marshal_check_blob (*p, 8, end)) - return FALSE; - *p += 8; - return TRUE; - case MONO_TYPE_STRING: - case MONO_MARSHAL_CATTR_TYPE_SYSTEM_TYPE: - return mono_marshal_skip_ser_string (p, end); - default: + if (data [2] != 0x01 || data [3] != 0x00) return FALSE; - } -} - -static gboolean -mono_marshal_skip_cattr_named_arg_value (const char **p, const char *end, guint8 type, guint8 array_type) -{ - if (type != MONO_TYPE_SZARRAY) - return mono_marshal_skip_cattr_element_value (p, end, type); - - if (!mono_marshal_check_blob (*p, 4, end)) + if (data [4] != 0x53 || data [5] != 0x50) return FALSE; - - guint32 len = mono_marshal_read32 (*p); - *p += 4; - if (len == 0xFFFFFFFF) - return TRUE; - - for (guint32 i = 0; i < len; ++i) { - if (!mono_marshal_skip_cattr_element_value (p, end, array_type)) - return FALSE; - } - - return TRUE; -} - -static gboolean -mono_marshal_set_callconv_from_unmanaged_callers_only_attribute_data (MonoCustomAttrEntry *attr, MonoMethod *method, MonoMethodSignature *csig, MonoError *error) -{ - error_init (error); - - const char *p = (const char*)attr->data; - const char *end = p + attr->data_size; - - if (!mono_marshal_check_blob (p, 2, end) || mono_marshal_read16 (p) != 0x0001) + if (data [6] != associated_source_type_name_len) return FALSE; - p += 2; - - MonoMethodSignature *sig = mono_method_signature_internal (attr->ctor); - if (sig->param_count != 0) + if (memcmp (&data [7], "AssociatedSourceType", associated_source_type_name_len)) return FALSE; - if (!mono_marshal_check_blob (p, 2, end)) + value = (const char *)&data [7 + associated_source_type_name_len]; + end = (const char *)data + attr->data_size; + if (value >= end) return FALSE; +MONO_DISABLE_WARNING (4310) // cast truncates constant value + if (*value == (char)0xFF) + return value + 1 == end; +MONO_RESTORE_WARNING - guint16 num_named_args = mono_marshal_read16 (p); - p += 2; - - for (guint16 i = 0; i < num_named_args; ++i) { - if (!mono_marshal_check_blob (p, 2, end)) - return FALSE; - - guint8 named_type = (guint8)*p++; - guint8 data_type = (guint8)*p++; - guint8 array_type = 0; - - if (data_type == MONO_TYPE_SZARRAY) { - if (!mono_marshal_check_blob (p, 1, end)) - return FALSE; - array_type = (guint8)*p++; - } - - if ((data_type == MONO_TYPE_ENUM || (data_type == MONO_TYPE_SZARRAY && array_type == MONO_TYPE_ENUM)) && !mono_marshal_skip_ser_string (&p, end)) + if ((*value & 0x80) == 0) { + if (value + 1 > end) return FALSE; - - guint32 name_len; - if (!mono_marshal_decode_blob_size (&p, end, &name_len)) + } else if ((*value & 0xC0) == 0x80) { + if (value + 2 > end) return FALSE; - if (!mono_marshal_check_blob (p, name_len, end)) + } else if ((*value & 0xE0) == 0xC0) { + if (value + 4 > end) return FALSE; - - const char *name = p; - p += name_len; - - if (name_len != MONO_MARSHAL_CALLCONVS_NAME_LEN || strncmp (name, MONO_MARSHAL_CALLCONVS_NAME, name_len) != 0) { - if (!mono_marshal_skip_cattr_named_arg_value (&p, end, data_type, array_type)) - return FALSE; - continue; - } - - if (named_type != MONO_MARSHAL_CATTR_TYPE_FIELD || data_type != MONO_TYPE_SZARRAY || array_type != MONO_MARSHAL_CATTR_TYPE_SYSTEM_TYPE) - return FALSE; - if (!mono_marshal_check_blob (p, 4, end)) - return FALSE; - - guint32 callconv_count = mono_marshal_read32 (p); - p += 4; - if (callconv_count == 0xFFFFFFFF) - continue; - - if (callconv_count > 1) - g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute field %s, specified for method %s. Only the first calling convention will be taken into account", MONO_MARSHAL_CALLCONVS_NAME, method->name); - - for (guint32 callconv_index = 0; callconv_index < callconv_count; ++callconv_index) { - if (!mono_marshal_check_blob (p, 1, end)) - return FALSE; - -MONO_DISABLE_WARNING (4310) // cast truncates constant value - if (*p == (char)0xFF) { - p += 1; - continue; - } -MONO_RESTORE_WARNING - - guint32 type_name_len; - if (!mono_marshal_decode_blob_size (&p, end, &type_name_len)) - return FALSE; - if (!mono_marshal_check_blob (p, type_name_len, end)) - return FALSE; - - if (callconv_index == 0) { - char *type_name = g_strndup (p, type_name_len); - MonoAssemblyLoadContext *alc = mono_image_get_alc (mono_defaults.corlib); - MonoType *callconv_type = mono_reflection_type_from_name_checked (type_name, alc, mono_defaults.corlib, error); - g_free (type_name); - return_val_if_nok (error, FALSE); - - if (callconv_type) - mono_marshal_set_signature_callconv_from_attribute (csig, callconv_type, error); - return_val_if_nok (error, FALSE); - } - - p += type_name_len; - } + } else { + return FALSE; } - return TRUE; + value_len = mono_metadata_decode_blob_size (value, &value); + return value <= end && value_len <= (guint32)(end - value) && value + value_len == end; } -#undef MONO_MARSHAL_CATTR_TYPE_FIELD -#undef MONO_MARSHAL_CATTR_TYPE_SYSTEM_TYPE -#undef MONO_MARSHAL_CALLCONVS_NAME -#undef MONO_MARSHAL_CALLCONVS_NAME_LEN - static void mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *method, MonoMethodSignature *csig) { @@ -3724,9 +3523,24 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met } if (attr != NULL) { - if (!mono_marshal_set_callconv_from_unmanaged_callers_only_attribute_data (attr, method, csig, error) && is_ok (error)) - mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Binary format of UnmanagedCallersOnlyAttribute on method %s was invalid.", method->name); - mono_error_assert_ok (error); + if (!mono_marshal_unmanaged_callers_only_has_only_associated_source_type (attr)) { + MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, error); + mono_error_assert_ok (error); + for (int i = 0; i < decoded_args->named_args_num; ++i) { + if (decoded_args->named_args_info [i].field && !strcmp (decoded_args->named_args_info [i].field->name, "CallConvs")) { + g_assertf(decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "UnmanagedCallersOnlyAttribute parameter %s must be an array, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); + MonoCustomAttrValueArray *calling_conventions = decoded_args->named_args[i]->value.array; + if (calling_conventions->len > 0) { + if (calling_conventions->len > 1) + g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute parameter %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name); + // TODO: Support multiple conventions? + MonoType* calling_convention = (MonoType*)calling_conventions->values[0].value.primitive; + mono_marshal_set_signature_callconv_from_attribute (csig, calling_convention, error); + } + } + } + mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); + } } if (!cinfo->cached) From 1d9d3013f957594dea2e6830b5d44139d9c92b57 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 04:27:41 +0000 Subject: [PATCH 09/16] Polish minimal Mono UCO handling Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/mono/mono/metadata/marshal.c | 36 ++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index d2b878bd4f08a4..06d01d59c44a88 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3454,7 +3454,10 @@ static gboolean mono_marshal_unmanaged_callers_only_has_only_associated_source_type (MonoCustomAttrEntry *attr) { const guint8 *data = attr->data; + const guint8 field_named_arg = 0x53; + const guint8 system_type = 0x50; const guint32 associated_source_type_name_len = sizeof ("AssociatedSourceType") - 1; + /* Prolog, named argument count, named argument kind, data type, and single-byte name length. */ const guint32 minimum_data_size = 2 + 2 + 1 + 1 + 1 + associated_source_type_name_len; const char *value; const char *end; @@ -3466,7 +3469,7 @@ mono_marshal_unmanaged_callers_only_has_only_associated_source_type (MonoCustomA return FALSE; if (data [2] != 0x01 || data [3] != 0x00) return FALSE; - if (data [4] != 0x53 || data [5] != 0x50) + if (data [4] != field_named_arg || data [5] != system_type) return FALSE; if (data [6] != associated_source_type_name_len) return FALSE; @@ -3522,25 +3525,26 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met } } + if (attr != NULL && mono_marshal_unmanaged_callers_only_has_only_associated_source_type (attr)) + attr = NULL; + if (attr != NULL) { - if (!mono_marshal_unmanaged_callers_only_has_only_associated_source_type (attr)) { - MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, error); - mono_error_assert_ok (error); - for (int i = 0; i < decoded_args->named_args_num; ++i) { - if (decoded_args->named_args_info [i].field && !strcmp (decoded_args->named_args_info [i].field->name, "CallConvs")) { - g_assertf(decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "UnmanagedCallersOnlyAttribute parameter %s must be an array, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); - MonoCustomAttrValueArray *calling_conventions = decoded_args->named_args[i]->value.array; - if (calling_conventions->len > 0) { - if (calling_conventions->len > 1) - g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute parameter %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name); - // TODO: Support multiple conventions? - MonoType* calling_convention = (MonoType*)calling_conventions->values[0].value.primitive; - mono_marshal_set_signature_callconv_from_attribute (csig, calling_convention, error); - } + MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, error); + mono_error_assert_ok (error); + for (int i = 0; i < decoded_args->named_args_num; ++i) { + if (decoded_args->named_args_info [i].field && !strcmp (decoded_args->named_args_info [i].field->name, "CallConvs")) { + g_assertf(decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "UnmanagedCallersOnlyAttribute parameter %s must be an array, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); + MonoCustomAttrValueArray *calling_conventions = decoded_args->named_args[i]->value.array; + if (calling_conventions->len > 0) { + if (calling_conventions->len > 1) + g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute parameter %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name); + // TODO: Support multiple conventions? + MonoType* calling_convention = (MonoType*)calling_conventions->values[0].value.primitive; + mono_marshal_set_signature_callconv_from_attribute (csig, calling_convention, error); } } - mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); } + mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); } if (!cinfo->cached) From 28956ee6713666361496917590f87198c739d083 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 04:29:35 +0000 Subject: [PATCH 10/16] Fix Mono pointer declaration style Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/mono/mono/metadata/marshal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 06d01d59c44a88..43289f12e80737 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3539,7 +3539,7 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met if (calling_conventions->len > 1) g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute parameter %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name); // TODO: Support multiple conventions? - MonoType* calling_convention = (MonoType*)calling_conventions->values[0].value.primitive; + MonoType *calling_convention = (MonoType*)calling_conventions->values[0].value.primitive; mono_marshal_set_signature_callconv_from_attribute (csig, calling_convention, error); } } From 0a8a23501d4de1a7751f0ca7d5e68775e6eb4a4a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 04:31:35 +0000 Subject: [PATCH 11/16] Address Mono UCO validation comments Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/mono/mono/metadata/marshal.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 43289f12e80737..6557b8c1f2a832 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3459,7 +3459,8 @@ mono_marshal_unmanaged_callers_only_has_only_associated_source_type (MonoCustomA const guint32 associated_source_type_name_len = sizeof ("AssociatedSourceType") - 1; /* Prolog, named argument count, named argument kind, data type, and single-byte name length. */ const guint32 minimum_data_size = 2 + 2 + 1 + 1 + 1 + associated_source_type_name_len; - const char *value; + const char *value_start; + const char *value_after_size; const char *end; guint32 value_len; @@ -3476,30 +3477,30 @@ mono_marshal_unmanaged_callers_only_has_only_associated_source_type (MonoCustomA if (memcmp (&data [7], "AssociatedSourceType", associated_source_type_name_len)) return FALSE; - value = (const char *)&data [7 + associated_source_type_name_len]; + value_start = (const char *)&data [7 + associated_source_type_name_len]; end = (const char *)data + attr->data_size; - if (value >= end) + if (value_start >= end) return FALSE; MONO_DISABLE_WARNING (4310) // cast truncates constant value - if (*value == (char)0xFF) - return value + 1 == end; + if (*value_start == (char)0xFF) + return value_start + 1 == end; MONO_RESTORE_WARNING - if ((*value & 0x80) == 0) { - if (value + 1 > end) + if ((*value_start & 0x80) == 0) { + if (value_start + 1 > end) return FALSE; - } else if ((*value & 0xC0) == 0x80) { - if (value + 2 > end) + } else if ((*value_start & 0xC0) == 0x80) { + if (value_start + 2 > end) return FALSE; - } else if ((*value & 0xE0) == 0xC0) { - if (value + 4 > end) + } else if ((*value_start & 0xE0) == 0xC0) { + if (value_start + 4 > end) return FALSE; } else { return FALSE; } - value_len = mono_metadata_decode_blob_size (value, &value); - return value <= end && value_len <= (guint32)(end - value) && value + value_len == end; + value_len = mono_metadata_decode_blob_size (value_start, &value_after_size); + return value_after_size <= end && value_len <= (guint32)(end - value_after_size) && value_after_size + value_len == end; } static void @@ -3533,12 +3534,13 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met mono_error_assert_ok (error); for (int i = 0; i < decoded_args->named_args_num; ++i) { if (decoded_args->named_args_info [i].field && !strcmp (decoded_args->named_args_info [i].field->name, "CallConvs")) { - g_assertf(decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "UnmanagedCallersOnlyAttribute parameter %s must be an array, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); + g_assertf (decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "UnmanagedCallersOnlyAttribute parameter %s must be an array, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); MonoCustomAttrValueArray *calling_conventions = decoded_args->named_args[i]->value.array; if (calling_conventions->len > 0) { if (calling_conventions->len > 1) g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute parameter %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name); // TODO: Support multiple conventions? + // System.Type values are stored in the primitive union member by load_cattr_value_noalloc. MonoType *calling_convention = (MonoType*)calling_conventions->values[0].value.primitive; mono_marshal_set_signature_callconv_from_attribute (csig, calling_convention, error); } From c095a072a0b42e6c640c21d9c72ef09a9c3505f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 04:33:39 +0000 Subject: [PATCH 12/16] Address final Mono validation comments Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/mono/mono/metadata/marshal.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 6557b8c1f2a832..347aa58731358a 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3487,13 +3487,13 @@ MONO_DISABLE_WARNING (4310) // cast truncates constant value MONO_RESTORE_WARNING if ((*value_start & 0x80) == 0) { - if (value_start + 1 > end) + if (value_start + 1 >= end) return FALSE; } else if ((*value_start & 0xC0) == 0x80) { - if (value_start + 2 > end) + if (value_start + 2 >= end) return FALSE; } else if ((*value_start & 0xE0) == 0xC0) { - if (value_start + 4 > end) + if (value_start + 4 >= end) return FALSE; } else { return FALSE; @@ -3534,11 +3534,11 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met mono_error_assert_ok (error); for (int i = 0; i < decoded_args->named_args_num; ++i) { if (decoded_args->named_args_info [i].field && !strcmp (decoded_args->named_args_info [i].field->name, "CallConvs")) { - g_assertf (decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "UnmanagedCallersOnlyAttribute parameter %s must be an array, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); + g_assertf (decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "UnmanagedCallersOnlyAttribute field %s must be an array, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); MonoCustomAttrValueArray *calling_conventions = decoded_args->named_args[i]->value.array; if (calling_conventions->len > 0) { if (calling_conventions->len > 1) - g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute parameter %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name); + g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute field %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name); // TODO: Support multiple conventions? // System.Type values are stored in the primitive union member by load_cattr_value_noalloc. MonoType *calling_convention = (MonoType*)calling_conventions->values[0].value.primitive; From d3daa1f8a9055a17c9b2968a3e32c8f4577f7624 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 04:35:45 +0000 Subject: [PATCH 13/16] Apply Mono helper review cleanup Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/mono/mono/metadata/marshal.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 347aa58731358a..01500cc327fdc7 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3451,7 +3451,7 @@ mono_marshal_set_signature_callconv_from_attribute(MonoMethodSignature *sig, Mon } static gboolean -mono_marshal_unmanaged_callers_only_has_only_associated_source_type (MonoCustomAttrEntry *attr) +mono_marshal_unmanaged_callers_only_attribute_has_only_associated_source_type (MonoCustomAttrEntry *attr) { const guint8 *data = attr->data; const guint8 field_named_arg = 0x53; @@ -3487,20 +3487,20 @@ MONO_DISABLE_WARNING (4310) // cast truncates constant value MONO_RESTORE_WARNING if ((*value_start & 0x80) == 0) { - if (value_start + 1 >= end) + if (value_start + 1 > end) return FALSE; } else if ((*value_start & 0xC0) == 0x80) { - if (value_start + 2 >= end) + if (value_start + 2 > end) return FALSE; } else if ((*value_start & 0xE0) == 0xC0) { - if (value_start + 4 >= end) + if (value_start + 4 > end) return FALSE; } else { return FALSE; } value_len = mono_metadata_decode_blob_size (value_start, &value_after_size); - return value_after_size <= end && value_len <= (guint32)(end - value_after_size) && value_after_size + value_len == end; + return value_after_size <= end && value_len <= (guint32)(end - value_after_size) && value_after_size + value_len <= end; } static void @@ -3526,7 +3526,7 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met } } - if (attr != NULL && mono_marshal_unmanaged_callers_only_has_only_associated_source_type (attr)) + if (attr != NULL && mono_marshal_unmanaged_callers_only_attribute_has_only_associated_source_type (attr)) attr = NULL; if (attr != NULL) { @@ -3534,7 +3534,7 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met mono_error_assert_ok (error); for (int i = 0; i < decoded_args->named_args_num; ++i) { if (decoded_args->named_args_info [i].field && !strcmp (decoded_args->named_args_info [i].field->name, "CallConvs")) { - g_assertf (decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "UnmanagedCallersOnlyAttribute field %s must be an array, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); + g_assertf (decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "Expected UnmanagedCallersOnlyAttribute field %s to be SZARRAY type, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); MonoCustomAttrValueArray *calling_conventions = decoded_args->named_args[i]->value.array; if (calling_conventions->len > 0) { if (calling_conventions->len > 1) From 1ae305156d3ad223c59140576b21369d1564ce7c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 04:37:45 +0000 Subject: [PATCH 14/16] Clarify Mono UCO helper checks Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/mono/mono/metadata/marshal.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 01500cc327fdc7..c98137e6a537de 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3454,6 +3454,8 @@ static gboolean mono_marshal_unmanaged_callers_only_attribute_has_only_associated_source_type (MonoCustomAttrEntry *attr) { const guint8 *data = attr->data; + const guint8 custom_attribute_prolog = 0x01; + const guint8 named_arg_count = 0x01; const guint8 field_named_arg = 0x53; const guint8 system_type = 0x50; const guint32 associated_source_type_name_len = sizeof ("AssociatedSourceType") - 1; @@ -3466,9 +3468,9 @@ mono_marshal_unmanaged_callers_only_attribute_has_only_associated_source_type (M if (attr->data_size < minimum_data_size) return FALSE; - if (data [0] != 0x01 || data [1] != 0x00) + if (data [0] != custom_attribute_prolog || data [1] != 0x00) return FALSE; - if (data [2] != 0x01 || data [3] != 0x00) + if (data [2] != named_arg_count || data [3] != 0x00) return FALSE; if (data [4] != field_named_arg || data [5] != system_type) return FALSE; @@ -3500,7 +3502,7 @@ MONO_RESTORE_WARNING } value_len = mono_metadata_decode_blob_size (value_start, &value_after_size); - return value_after_size <= end && value_len <= (guint32)(end - value_after_size) && value_after_size + value_len <= end; + return value_after_size <= end && value_len <= (guint32)(end - value_after_size); } static void @@ -3534,7 +3536,7 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met mono_error_assert_ok (error); for (int i = 0; i < decoded_args->named_args_num; ++i) { if (decoded_args->named_args_info [i].field && !strcmp (decoded_args->named_args_info [i].field->name, "CallConvs")) { - g_assertf (decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "Expected UnmanagedCallersOnlyAttribute field %s to be SZARRAY type, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); + g_assertf (decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "Expected UnmanagedCallersOnlyAttribute field %s to be SZARRAY type, specified for method %s. This indicates a malformed attribute or incorrect usage pattern.", decoded_args->named_args_info [i].field->name, method->name); MonoCustomAttrValueArray *calling_conventions = decoded_args->named_args[i]->value.array; if (calling_conventions->len > 0) { if (calling_conventions->len > 1) From af7862f1c13a02213a27d6c2dae24dbfd0224ca1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 09:11:05 +0000 Subject: [PATCH 15/16] Fix Mono UCO attribute decode context Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/mono/mono/metadata/marshal.c | 60 +------------------------------- 1 file changed, 1 insertion(+), 59 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index c98137e6a537de..ac991c0c8cc0ef 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3450,61 +3450,6 @@ mono_marshal_set_signature_callconv_from_attribute(MonoMethodSignature *sig, Mon // - Adjust the code above with 'anding' the attribute parameter value } -static gboolean -mono_marshal_unmanaged_callers_only_attribute_has_only_associated_source_type (MonoCustomAttrEntry *attr) -{ - const guint8 *data = attr->data; - const guint8 custom_attribute_prolog = 0x01; - const guint8 named_arg_count = 0x01; - const guint8 field_named_arg = 0x53; - const guint8 system_type = 0x50; - const guint32 associated_source_type_name_len = sizeof ("AssociatedSourceType") - 1; - /* Prolog, named argument count, named argument kind, data type, and single-byte name length. */ - const guint32 minimum_data_size = 2 + 2 + 1 + 1 + 1 + associated_source_type_name_len; - const char *value_start; - const char *value_after_size; - const char *end; - guint32 value_len; - - if (attr->data_size < minimum_data_size) - return FALSE; - if (data [0] != custom_attribute_prolog || data [1] != 0x00) - return FALSE; - if (data [2] != named_arg_count || data [3] != 0x00) - return FALSE; - if (data [4] != field_named_arg || data [5] != system_type) - return FALSE; - if (data [6] != associated_source_type_name_len) - return FALSE; - if (memcmp (&data [7], "AssociatedSourceType", associated_source_type_name_len)) - return FALSE; - - value_start = (const char *)&data [7 + associated_source_type_name_len]; - end = (const char *)data + attr->data_size; - if (value_start >= end) - return FALSE; -MONO_DISABLE_WARNING (4310) // cast truncates constant value - if (*value_start == (char)0xFF) - return value_start + 1 == end; -MONO_RESTORE_WARNING - - if ((*value_start & 0x80) == 0) { - if (value_start + 1 > end) - return FALSE; - } else if ((*value_start & 0xC0) == 0x80) { - if (value_start + 2 > end) - return FALSE; - } else if ((*value_start & 0xE0) == 0xC0) { - if (value_start + 4 > end) - return FALSE; - } else { - return FALSE; - } - - value_len = mono_metadata_decode_blob_size (value_start, &value_after_size); - return value_after_size <= end && value_len <= (guint32)(end - value_after_size); -} - static void mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *method, MonoMethodSignature *csig) { @@ -3528,11 +3473,8 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met } } - if (attr != NULL && mono_marshal_unmanaged_callers_only_attribute_has_only_associated_source_type (attr)) - attr = NULL; - if (attr != NULL) { - MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, error); + MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (m_class_get_image (method->klass), attr->ctor, attr->data, attr->data_size, error); mono_error_assert_ok (error); for (int i = 0; i < decoded_args->named_args_num; ++i) { if (decoded_args->named_args_info [i].field && !strcmp (decoded_args->named_args_info [i].field->name, "CallConvs")) { From 7ea546a576763e8a58722cb7c26388e43c6eccf7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 12:26:55 +0000 Subject: [PATCH 16/16] Minimize Mono marshal fix diff Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/mono/mono/metadata/marshal.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index ac991c0c8cc0ef..821ac6322f6d4f 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3478,14 +3478,13 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met mono_error_assert_ok (error); for (int i = 0; i < decoded_args->named_args_num; ++i) { if (decoded_args->named_args_info [i].field && !strcmp (decoded_args->named_args_info [i].field->name, "CallConvs")) { - g_assertf (decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "Expected UnmanagedCallersOnlyAttribute field %s to be SZARRAY type, specified for method %s. This indicates a malformed attribute or incorrect usage pattern.", decoded_args->named_args_info [i].field->name, method->name); + g_assertf(decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "UnmanagedCallersOnlyAttribute parameter %s must be an array, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); MonoCustomAttrValueArray *calling_conventions = decoded_args->named_args[i]->value.array; if (calling_conventions->len > 0) { if (calling_conventions->len > 1) - g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute field %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name); + g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute parameter %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name); // TODO: Support multiple conventions? - // System.Type values are stored in the primitive union member by load_cattr_value_noalloc. - MonoType *calling_convention = (MonoType*)calling_conventions->values[0].value.primitive; + MonoType* calling_convention = (MonoType*)calling_conventions->values[0].value.primitive; mono_marshal_set_signature_callconv_from_attribute (csig, calling_convention, error); } }