How to access anonymous fields in a dynamic assembly?
Asked Answered
S

2

2

.net Framework 4.7.2...

Compiled expressions can access private fields. When I use exactly the same expression and write it to a dynamic assembly, using CompileToMethod, I get a System.FieldAccessException while trying to read the private field.

Is there anything I can do to allow the dynamic assembly to have the same access privileges that a compiled expression does? There is ancient lore that says you cannot. But I can't find anything resembling a primary source for that claim. I can't believe there is not some form of assembly attributes or permissioning that would allow access.

Can I do it if I save the assembly instead? (Writing cached marshalling assemblies to disk is a likely feature in the future).

The application is marshalling of structs to streams in a domain-specific computer music language. Serialization is not an option (another example of dynamic code in dynamic assemblies that violates access).

Example code:

The lambda expression successfully reads the value of the private field of ComplexStruct (given below). If the same expression is emitted to a dynamic assembly using CompileToMethod, it fails with an access exception.

    ComplexStruct s = new ComplexStruct();

    s.String1 = "abc";

    // Pick a private field (one of the backing fields for a property)
    FieldInfo fieldInfo = typeof(ComplexStruct).GetFields(BindingFlags.NonPublic | BindingFlags.Instance)[0];

    var structArgument = Expression.Parameter(typeof(ComplexStruct));

    var lambda = Expression.Lambda<Func<ComplexStruct,String>>(
        Expression.Field(structArgument, fieldInfo), // return the value of the private field.
        structArgument);
    Func<ComplexStruct,String> fn = lambda.Compile();

    String result = fn(s);
    Assert.AreEqual(structArgument.String1, result);

A struct with private fields:

// (Complex compared to simple struct where all fields 
// are public or the struct is unmanaged in case you were wondering)
public struct ComplexStruct : IEquatable<ComplexStruct>
{

    public String String1 { get; set; } // the backing field for this property gets read.
    public String String2 { get; set; }
    public String String3 { get;  }

    public ComplexStruct(String v1, String v2)
    {
        String1 = v1;
        String2 = v2;
    }

    public bool Equals(ComplexStruct other)
    {
        return String1 == other.String1 && String2 == other.String2;
    }
}

Creation of the assembly:

    AppDomain myAppDomain = Thread.GetDomain();
    AssemblyName myAsmName = new AssemblyName();
    myAsmName.Name = "DynamicAssembly";

    this.saveAssembly = ServiceBase.DEBUG;

    assemblyBuilder = myAppDomain.DefineDynamicAssembly(
                         myAsmName,
                         saveAssembly? AssemblyBuilderAccess.RunAndSave: AssemblyBuilderAccess.RunAndCollect);
Santos answered 28/4, 2020 at 17:18 Comment(2)
One hint could be the ability of DynamicMethods to skip visibility checks. Have a look at the constructor list, there are a few constructors that have a skipVisibility boolean parameter. Ages ago i tried to find out how and why that works, but i got lost in process. Also have a look at the ownerType or module parameters there: learn.microsoft.com/en-us/dotnet/api/…Taken
@Taken Yes. That seems to be the backdoor for Expressions. Extraordinary!Santos
M
2

See my answer to this question on more details on the undocumented IgnoreAccessCheckToAttribute which is doing what you probably want.

Here is sample code:

One legitimate use is when compiling dynamically assemblies that needs access to internal classes, as in the following code:

var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(name),
                                                   AssemblyBuilderAccess.RunAndCollect);

var assmemblyNames = typesToAccess.Where(t => t is not null)
    .Select(t => t!.Assembly.FullName.Split(',').First()) // Extract the name from fullName
    .Distinct().ToList();

var ignoreAccessCtor = typeof(IgnoresAccessChecksToAttribute)
                              .GetConstructor(new Type[] { typeof(string) });

foreach (var refName in assemblyNames)
{
      var builder = new CustomAttributeBuilder(ignoreAccessCtor, new object[] { refName });
      assemblyBuilder.SetCustomAttribute(builder);
}
Mages answered 17/6, 2022 at 1:5 Comment(0)
S
1

After reviewing .net sources it seems pretty certain that there is no way for an Assembly to bypass field access checks.

thehennyy pointed out the backdoor that Linq Expressions use. DynamicMethod constructors provides a skipVisibility parameter which allows generation of IL that can access non-public fields and methods. But there's no way to integrate DynamicMethods with Dynamic Assemblies, or emit them to a saved assembly.

Given the limitations of DynamicMethods, there doesn't seem to be any reason to prefer them over Linq Expressions, given that Linq Expression APIs are infinitely easier to use that IL.Emit APIs.

In the end, I used call-outs to templated classes that generate struct serialization delegates produced by Linq Expressions in static constructors.

If you're following the same path, you may want to look at "unmanaged" structs, introduced in C# 7.2, which allow optimized serialization of structs composed entirely of ValueType members. Given that Strings are reference classes, that usually has limited value. But given that I'm trying to write allocation-free serializers, they were useful for my purposes.

Santos answered 1/5, 2020 at 19:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.