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?
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.