Search code examples
c#.netopen-sourcelog4netpeverify

log4net doesn't pass verification when compiling


https://github.com/apache/log4net

I am compiling log4net from the source above, but it doesn't pass verification:

[IL]: Error: [log4net.dll : log4net.Plugin.RemoteLoggingServerPlugin::Attach][offset 0x00000029] Method is not visible.

Code is ok:

public interface ILoggerRepository
{
    ...
}

public interface IPlugin
{
    void Attach(ILoggerRepository repository);
}

public abstract class PluginSkeleton : IPlugin
{
    public virtual void Attach(ILoggerRepository repository) { }
}

public class RemoteLoggingServerPlugin : PluginSkeleton
{
    override public void Attach(ILoggerRepository repository)
    {
        base.Attach(repository);
        ...
    }
}

https://github.com/apache/log4net/blob/trunk/src/Plugin/IPlugin.cs

https://github.com/apache/log4net/blob/trunk/src/Plugin/PluginSkeleton.cs

https://github.com/apache/log4net/blob/trunk/src/Plugin/RemoteLoggingServerPlugin.cs

Investigation shows that it fails in calling RemotingServices.Marshal():

override public void Attach(ILoggerRepository repository)
{
    base.Attach(repository);

    // Create the sink and marshal it
    m_sink = new RemoteLoggingSinkImpl(repository);

    try
    {
         **RemotingServices.Marshal(m_sink, m_sinkUri, typeof(IRemoteLoggingSink));**
    }
    catch(Exception ex)
    {
        LogLog.Error(declaringType, "Failed to Marshal remoting sink", ex);
    }
}

But there is nothing crucial here. Moreover calling RemotingServices.Marshal() with any type leads to the same problems:

Even if I change the Attach() to this:

override public void Attach(ILoggerRepository repository)
{
    RemotingServices.Marshal(null, null, typeof(int));
}

Can someone spot what is the problem?


Solution

  • The problem is related to the fact that with .NET 4 Level 2 transparency was introduced. (See http://msdn.microsoft.com/en-us/library/dd233102.aspx for details.)

    The method override public void Attach(ILoggerRepository repository) is lacking the SecuritySafeCriticalAttribute. Adding the attribute:

    #if NET_4_0
        [System.Security.SecuritySafeCritical]
    #endif
        override public void Attach(ILoggerRepository repository)
        {
            // ...
        }
    

    will make the IL verification pass. (Also see: http://msdn.microsoft.com/en-us/library/bb397858.aspx for further information.)

    Update: To shed some more light on why verification fails (which might not be immediately clear by just reading the articles in the links provided) here is a short explanation.

    RemotingServices.Marshal has the [SecuritySafeCritical] attribute applied. So one would assume that calling the method from a transparent method would be allowed. However RemotingServices.Marshal returns an object of type System.Runtime.Remoting.ObjRef and said type is annotated with the [SecurityCritical] attribute.
    If the log4net code would store a reference to the returned value in a local variable, Code Analysis would detect the error and issue a CA2140 warning ("Transparent code must not reference security critical items").
    Now apparently under the security transparency rules, a transparent method may not call a security safe-critical method if the called method returns a security critical type even if the transparent method does not store a reference to the returned object as the following sample demonstrates:

    public class TransparencyRulesDemo
    {
        [SecuritySafeCritical]
        public void SafeGetCritical()
        {
            GetCritical();
        }
    
        public void TransparentGetCritical()
        {
            // Below line will trigger a CA2140 warning if uncommented...
            // var critical = GetCritical();
    
            // ...the following line on the other hand will not produce any warning
            // but will lead to IL verification errors and MethodAccessExceptions if
            // called from transparent code.
            GetCritical();
        }
    
        [SecuritySafeCritical]
        public Critical GetCritical()
        {
            return new Critical();
        }
    }
    
    [SecurityCritical]
    public class Critical
    {
    
    } 
    

    This btw. makes the [SecuritySafeCritical] attribute on RemotingServices.Marshal kind of pointless.