Skip to content
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

Mono ignores non-public custom attributes in dynamic assemblies #60650

Closed
elinor-fung opened this issue Oct 19, 2021 · 8 comments · Fixed by #87406
Closed

Mono ignores non-public custom attributes in dynamic assemblies #60650

elinor-fung opened this issue Oct 19, 2021 · 8 comments · Fixed by #87406
Assignees
Labels
area-VM-reflection-mono Reflection issues specific to MonoVM
Milestone

Comments

@elinor-fung
Copy link
Member

elinor-fung commented Oct 19, 2021

Description

When building a dynamic assembly via reflection with the various *Builder classes, non-public attributes are explicitly ignored in mono.

In mono_custom_attrs_from_builders_handle:

for (int i = 0; i < count; ++i) {
MONO_HANDLE_ARRAY_GETREF (cattr, cattrs, i);
if (!custom_attr_visible (image, cattr, ctor_handle, &ctor_method))
continue;

There's a comment about it matching .NET, but that is not what I see with coreclr or .NET Framework.

/* Skip nonpublic attributes since MS.NET seems to do the same */

Not sure if there's another reason mono skips non-public attributes?

Reproduction Steps

Dynamic assembly with an assembly-level non-public attribute:

AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("DynamicAssembly"), AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");

// Create internal attribute
TypeBuilder typeBuilder = moduleBuilder.DefineType("InternalAttribute", TypeAttributes.NotPublic, typeof(Attribute));
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[0]);
constructorBuilder.GetILGenerator().Emit(OpCodes.Ret);

// Add assembly-level attribute
ConstructorInfo constructorInfo = typeBuilder.CreateTypeInfo().GetConstructor(Array.Empty<Type>());
CustomAttributeBuilder customAttributeBuilder = new CustomAttributeBuilder(constructorInfo, Array.Empty<object>());
assemblyBuilder.SetCustomAttribute(customAttributeBuilder);

IList<CustomAttributeData> attrs = assemblyBuilder.GetCustomAttributesData();
Console.WriteLine($"{assemblyBuilder.GetName().Name} - Attributes (count: {attrs.Count})");
foreach (CustomAttributeData attr in attrs)
{
    Console.WriteLine($"  {attr}");
}

Expected behavior

The internal attribute is listed. On coreclr (and also .NET Framework 4.6.2), the above prints:

DynamicAssembly - Attributes (count: 1)
  [InternalAttribute()]

Actual behavior

The internal attribute is not listed. On mono, the above prints:

DynamicAssembly - Attributes (count: 0)

Other information

This results in the test infrastructure for Roslyn analyzers (in dotnet/roslyn-sdk) being unable to run on mono.

https://github.com/dotnet/roslyn-sdk/blob/27fe3d6060150031b532cc7255259c40a37a5fda/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerInfo.cs

They rely on emitting a non-public IgnoresAccessChecksToAttribute to skip access checks. Since mono ends up ignoring the attribute, using their test infrastructure results in:

System.MethodAccessException : Method `Microsoft.CodeAnalysis.Testing.AnalyzerInfo+CustomAnalysisContext..ctor()' is inaccessible from method `CustomAnalysisContextImpl..ctor()'
    Stack Trace:
        at CustomAnalysisContextImpl..ctor()

cc @lambdageek

@elinor-fung elinor-fung added the area-VM-reflection-mono Reflection issues specific to MonoVM label Oct 19, 2021
@ghost
Copy link

ghost commented Oct 19, 2021

Tagging subscribers to this area:
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

When building a dynamic assembly via reflection with the various *Builder classes, non-public attributes are explicitly ignored in mono.

In mono_custom_attrs_from_builders_handle:

for (int i = 0; i < count; ++i) {
MONO_HANDLE_ARRAY_GETREF (cattr, cattrs, i);
if (!custom_attr_visible (image, cattr, ctor_handle, &ctor_method))
continue;

There's a comment about it matching .NET, but that is not what I see with coreclr or .NET Framework.

/* Skip nonpublic attributes since MS.NET seems to do the same */

Reproduction Steps

Dynamic assembly with an assembly-level non-public attribute:

AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("DynamicAssembly"), AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");

// Create internal attribute
TypeBuilder typeBuilder = moduleBuilder.DefineType("InternalAttribute", TypeAttributes.NotPublic, typeof(Attribute));
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[0]);
constructorBuilder.GetILGenerator().Emit(OpCodes.Ret);

// Add assembly-level attribute
ConstructorInfo constructorInfo = typeBuilder.CreateTypeInfo().GetConstructor(Array.Empty<Type>());
CustomAttributeBuilder customAttributeBuilder = new CustomAttributeBuilder(constructorInfo, Array.Empty<object>());
assemblyBuilder.SetCustomAttribute(customAttributeBuilder);

IList<CustomAttributeData> attrs = assemblyBuilder.GetCustomAttributesData();
Console.WriteLine($"{assemblyBuilder.GetName().Name} - Attributes (count: {attrs.Count})");
foreach (CustomAttributeData attr in attrs)
{
    Console.WriteLine($"  {attr}");
}

Expected behavior

The internal attribute is listed. On coreclr (and also .NET Framework 4.6.2), the above prints:

DynamicAssembly - Attributes (count: 1)
  [InternalAttribute()]

Actual behavior

The internal attribute is not listed. On mono, the above prints:

DynamicAssembly - Attributes (count: 0)

Regression?

No response

Known Workarounds

No response

Configuration

This results in the test infrastructure for Roslyn analyzers (in dotnet/roslyn-sdk) being unable to run on mono.

https://github.com/dotnet/roslyn-sdk/blob/27fe3d6060150031b532cc7255259c40a37a5fda/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerInfo.cs

They rely on emitting a non-public IgnoresAccessChecksToAttribute to skip access checks. Since mono ends up ignoring the attribute, using their test infrastructure results in:

System.MethodAccessException : Method `Microsoft.CodeAnalysis.Testing.AnalyzerInfo+CustomAnalysisContext..ctor()' is inaccessible from method `CustomAnalysisContextImpl..ctor()'
    Stack Trace:
        at CustomAnalysisContextImpl..ctor()

Other information

No response

Author: elinor-fung
Assignees: -
Labels:

area-System.Reflection-mono

Milestone: -

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Oct 19, 2021
@lambdageek
Copy link
Member

Not sure if there's another reason mono skips non-public attributes?

I doubt it.

That comment is from 2005 from mono/mono@6c9333e Perhaps it's matching a bug from some old version of .NET Framework.

@marek-safar marek-safar removed the untriaged New issue has not been triaged by the area owner label Jun 17, 2022
@marek-safar marek-safar added this to the 7.0.0 milestone Jun 17, 2022
@marek-safar
Copy link
Contributor

Targetting net7 to enable tests to run to possibly discover other issues

@ivanpovazan
Copy link
Member

@lambdageek this seems to be limited by a call to the local function custom_attr_visible here:

count_visible += custom_attr_visible (image, cattr, ctor_handle, &ctor_method);

as it covers only public attribute visibility:
if ((visibility != TYPE_ATTRIBUTE_PUBLIC) && (visibility != TYPE_ATTRIBUTE_NESTED_PUBLIC))
return FALSE;

do you see any major implications if we would just try to loosen the limitation?

@lambdageek
Copy link
Member

lambdageek commented Jun 5, 2023

@ivanpovazan I think you're onto something with custom_attr_visible. I think what might be wrong is that we expect this check to be true in this case (the attributeInternalAttribute is defined in the assembly builder and is applied in the same assembly builder - so this should be true):

if (m_class_get_image (klass) != image) {

But note that actually the attribute is defined in the moduleBuilder, but it's applied in the assemblyBuilder. Due to the way SRE works in .NET, those are actually two different MonoImages. so this comparison is actually false. For assmebly-level attributes we probably need to also compare m_class_get_image (klass)->assembly->image to image - I think that will end up pointing to the assemblyBuilder related to the moduleBuilder.

@tmds
Copy link
Member

tmds commented Jun 12, 2023

@lambdageek do you expect this to be fixed on the short term (or for .NET 8)? This issue also affects the aspnetcore tests, so we're looking into skipping the tests there too (dotnet/aspnetcore#48710).

cc @Sapana-Khemkar @omajid

@ivanpovazan
Copy link
Member

@tmds we are currently looking into this and will try to get the fix upstreamed by preview 6 snap

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jun 12, 2023
@ivanpovazan
Copy link
Member

ivanpovazan commented Jun 20, 2023

Just an update: the fix will most probably not be ready for preview 6 snap, but we actively working on resolving the issue.

EDIT : Aiming to resolve the issue for .NET8

@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jun 22, 2023
ivanpovazan added a commit that referenced this issue Jul 4, 2023
… attributes in dynamic assemblies (#88322)

* Reenable disabled tests
* Disable Roslyn analyzer tests in the browser as Monitor.Wait / Monitor.Pulse are not supported on single-threaded WASM

Contributes to: #60650
@ghost ghost locked as resolved and limited conversation to collaborators Jul 22, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-VM-reflection-mono Reflection issues specific to MonoVM
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants