Skip to content
Browse files

NH-2857: Medium Trust Bug in 3.2 - changed proxy generator to call ba…

…se-class constructor where available
  • Loading branch information...
1 parent cc10532 commit d259996bf28422efe66656431ae19004422a287b @FlukeFan FlukeFan committed Jan 23, 2012
View
BIN Tools/PEVerify/PEVerify.exe
Binary file not shown.
View
6 Tools/PEVerify/PEVerify.exe.config
@@ -0,0 +1,6 @@
+<?xml version ="1.0"?>
+<configuration>
+ <startup>
+ <requiredRuntime safemode="true" imageVersion="v2.0.50727" version="v2.0.50727"/>
+ </startup>
+</configuration>
View
64 src/NHibernate.Test/DynamicProxyTests/PeVerifier.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using NUnit.Framework;
+
+namespace NHibernate.Test.DynamicProxyTests
+{
+ // utility class to run PEVerify.exe against a saved-to-disk assembly, similar to:
+ // http://stackoverflow.com/questions/7290893/is-there-an-api-for-verifying-the-msil-of-a-dynamic-assembly-at-runtime
+ public class PeVerifier
+ {
+ private string _assemlyLocation;
+ private string _peVerifyPath;
+
+ public PeVerifier(string assemblyFileName)
+ {
+ var assemblyLocation = Path.Combine(Environment.CurrentDirectory, assemblyFileName);
+
+ if (!File.Exists(assemblyLocation))
+ throw new ArgumentException(string.Format("Could not locate assembly {0}", assemblyLocation), "assemblyLocation");
+
+ _assemlyLocation = assemblyLocation;
+
+ var dir = Path.GetDirectoryName(_assemlyLocation);
+
+ while (!Directory.Exists(Path.Combine(dir, "Tools/PEVerify")))
+ {
+ if (Directory.GetParent(dir) == null)
+ throw new Exception(string.Format("Could not find Tools/PEVerify directory in ancestor of {0}", _assemlyLocation));
+
+ dir = Directory.GetParent(dir).FullName;
+ }
+
+ _peVerifyPath = Path.Combine(dir, "Tools/PEVerify/PEVerify.exe");
+
+ if (!File.Exists(_peVerifyPath))
+ throw new Exception(string.Format("Could not find PEVerify.exe at {0}", _peVerifyPath));
+ }
+
+ public void AssertIsValid()
+ {
+ var process = new Process
+ {
+ StartInfo =
+ {
+ FileName = _peVerifyPath,
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ Arguments = "\"" + _assemlyLocation + "\" /VERBOSE",
+ CreateNoWindow = true
+ }
+ };
+
+ process.Start();
+ var processOutput = process.StandardOutput.ReadToEnd();
+ process.WaitForExit();
+
+ var result = process.ExitCode + " code ";
+
+ if (process.ExitCode != 0)
+ Assert.Fail("PeVerify reported error(s): " + Environment.NewLine + processOutput, result);
+ }
+ }
+}
View
145 src/NHibernate.Test/DynamicProxyTests/PeVerifyFixture.cs
@@ -0,0 +1,145 @@
+using System;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+using NUnit.Framework;
+using NHibernate.Proxy.DynamicProxy;
+
+namespace NHibernate.Test.DynamicProxyTests
+{
+ public class PeVerifyFixture
+ {
+ private static bool wasCalled;
+
+ private const string assemblyName = "peVerifyAssembly";
+ private const string assemblyFileName = "peVerifyAssembly.dll";
+
+ [Test]
+ public void VerifyClassWithPublicConstructor()
+ {
+ var factory = new ProxyFactory(new SavingProxyAssemblyBuilder(assemblyName));
+ var proxyType = factory.CreateProxyType(typeof(ClassWithPublicDefaultConstructor), null);
+
+ wasCalled = false;
+ Activator.CreateInstance(proxyType);
+
+ Assert.That(wasCalled);
+ new PeVerifier(assemblyFileName).AssertIsValid();
+ }
+
+ [Test]
+ public void VerifyClassWithProtectedConstructor()
+ {
+ var factory = new ProxyFactory(new SavingProxyAssemblyBuilder(assemblyName));
+ var proxyType = factory.CreateProxyType(typeof(ClassWithProtectedDefaultConstructor), null);
+
+ wasCalled = false;
+ Activator.CreateInstance(proxyType);
+
+ Assert.That(wasCalled);
+ new PeVerifier(assemblyFileName).AssertIsValid();
+ }
+
+ [Test]
+ public void VerifyClassWithPrivateConstructor()
+ {
+ var factory = new ProxyFactory(new SavingProxyAssemblyBuilder(assemblyName));
+ var proxyType = factory.CreateProxyType(typeof(ClassWithPrivateDefaultConstructor), null);
+
+ wasCalled = false;
+ Activator.CreateInstance(proxyType);
+
+ Assert.That(!wasCalled); // System.Object constructor called - works, but fails PeVerify
+ }
+
+ [Test]
+ public void VerifyClassWithNoDefaultConstructor()
+ {
+ var factory = new ProxyFactory(new SavingProxyAssemblyBuilder(assemblyName));
+ var proxyType = factory.CreateProxyType(typeof(ClassWithNoDefaultConstructor), null);
+
+ wasCalled = false;
+ Activator.CreateInstance(proxyType);
+
+ Assert.That(!wasCalled); // System.Object constructor called - works, but fails PeVerify
+ }
+
+ [Test]
+ public void VerifyClassWithInternalConstructor()
+ {
+ var factory = new ProxyFactory(new SavingProxyAssemblyBuilder(assemblyName));
+ var proxyType = factory.CreateProxyType(typeof(ClassWithInternalConstructor), null);
+
+ wasCalled = false;
+ Activator.CreateInstance(proxyType);
+
+ Assert.That(!wasCalled); // System.Object constructor called - works, but fails PeVerify
+ }
+
+ #region PeVerifyTypes
+
+ public class ClassWithPublicDefaultConstructor
+ {
+ public ClassWithPublicDefaultConstructor() { InitG<int>(1); }
+ public ClassWithPublicDefaultConstructor(int unused) { }
+ public virtual int Prop1 { get; set; }
+ public virtual void InitG<T>(T value) { Init((int)(object)value); }
+ public virtual void Init(int value) { Prop1 = value; if (Prop1 == 1) wasCalled = true; }
+ }
+
+ public class ClassWithProtectedDefaultConstructor
+ {
+ protected ClassWithProtectedDefaultConstructor() { wasCalled = true; }
+ }
+
+ public class ClassWithPrivateDefaultConstructor
+ {
+ private ClassWithPrivateDefaultConstructor() { wasCalled = true; }
+ }
+
+ public class ClassWithNoDefaultConstructor
+ {
+ public ClassWithNoDefaultConstructor(int unused) { wasCalled = true; }
+ public ClassWithNoDefaultConstructor(string unused) { wasCalled = true; }
+ }
+
+ public class ClassWithInternalConstructor
+ {
+ internal ClassWithInternalConstructor() { wasCalled = true; }
+ }
+
+ #endregion
+
+ #region ProxyFactory.IProxyAssemblyBuilder
+
+ public class SavingProxyAssemblyBuilder : IProxyAssemblyBuilder
+ {
+ private string assemblyName;
+ private AssemblyBuilder assemblyBuilder;
+
+ public SavingProxyAssemblyBuilder(string assemblyName)
+ {
+ this.assemblyName = assemblyName;
+ }
+
+ public AssemblyBuilder DefineDynamicAssembly(AppDomain appDomain, AssemblyName name)
+ {
+ AssemblyBuilderAccess access = AssemblyBuilderAccess.RunAndSave;
+ assemblyBuilder = appDomain.DefineDynamicAssembly(new AssemblyName(assemblyName), access);
+ return assemblyBuilder;
+ }
+
+ public ModuleBuilder DefineDynamicModule(string moduleName)
+ {
+ return assemblyBuilder.DefineDynamicModule(moduleName, string.Format("{0}.mod", assemblyName), true);
+ }
+
+ public void Save()
+ {
+ assemblyBuilder.Save(assemblyName + ".dll");
+ }
+ }
+
+ #endregion
+ }
+}
View
2 src/NHibernate.Test/NHibernate.Test.csproj
@@ -224,13 +224,15 @@
<Compile Include="DriverTest\Sql2008DateTime2Test.cs" />
<Compile Include="DriverTest\SqlClientDriverFixture.cs" />
<Compile Include="DriverTest\SqlServerCeDriverFixture.cs" />
+ <Compile Include="DynamicProxyTests\PeVerifyFixture.cs" />
<Compile Include="DynamicProxyTests\GenericMethodsTests\GenericMethodShouldBeProxied.cs" />
<Compile Include="DynamicProxyTests\InterfaceProxySerializationTests\IMyProxy.cs" />
<Compile Include="DynamicProxyTests\InterfaceProxySerializationTests\MyProxyImpl.cs" />
<Compile Include="DynamicProxyTests\InterfaceProxySerializationTests\ProxyFixture.cs" />
<Compile Include="DynamicProxyTests\InterfaceWithEqualsGethashcodeTests.cs" />
<Compile Include="DynamicProxyTests\LazyFieldInterceptorSerializable.cs" />
<Compile Include="DynamicProxyTests\PassThroughInterceptor.cs" />
+ <Compile Include="DynamicProxyTests\PeVerifier.cs" />
<Compile Include="DynamicProxyTests\ProxiedMembers\Fixture.cs" />
<Compile Include="DynamicProxyTests\ProxiedMembers\MetodWithRefDictionaryTest.cs" />
<Compile Include="EngineTest\CallableParserFixture.cs" />
View
2 src/NHibernate/NHibernate.csproj
@@ -542,6 +542,7 @@
<Compile Include="Proxy\DefaultProxyFactory.cs" />
<Compile Include="Proxy\DynamicProxy\DefaultArgumentHandler.cs" />
<Compile Include="Proxy\DynamicProxy\DefaultMethodEmitter.cs" />
+ <Compile Include="Proxy\DynamicProxy\DefaultProxyAssemblyBuilder.cs" />
<Compile Include="Proxy\DynamicProxy\DefaultProxyMethodBuilder.cs" />
<Compile Include="Proxy\DynamicProxy\HashSetExtensions.cs" />
<Compile Include="Proxy\DynamicProxy\IArgumentHandler.cs" />
@@ -551,6 +552,7 @@
<Compile Include="Proxy\DynamicProxy\InvocationHandler.cs" />
<Compile Include="Proxy\DynamicProxy\InvocationInfo.cs" />
<Compile Include="Proxy\DynamicProxy\IProxy.cs" />
+ <Compile Include="Proxy\DynamicProxy\IProxyAssemblyBuilder.cs" />
<Compile Include="Proxy\DynamicProxy\IProxyCache.cs" />
<Compile Include="Proxy\DynamicProxy\IProxyMethodBuilder.cs" />
<Compile Include="Proxy\DynamicProxy\OpCodesMap.cs" />
View
12 src/NHibernate/Proxy/DynamicProxy/DefaultMethodEmitter.cs
@@ -64,16 +64,22 @@ public void EmitMethodBody(ILGenerator IL, MethodInfo method, FieldInfo field)
IL.Emit(OpCodes.Callvirt, getInterceptor);
// if (interceptor == null)
- // throw new NullReferenceException();
+ // return base.method(...);
Label skipThrow = IL.DefineLabel();
IL.Emit(OpCodes.Dup);
IL.Emit(OpCodes.Ldnull);
IL.Emit(OpCodes.Bne_Un, skipThrow);
- IL.Emit(OpCodes.Newobj, notImplementedConstructor);
- IL.Emit(OpCodes.Throw);
+ IL.Emit(OpCodes.Pop); // get rid of the reference to the duplicated interceptor
+ IL.Emit(OpCodes.Ldarg_0);
+
+ for(int i=0; i<method.GetParameters().Length; i++)
+ IL.Emit(OpCodes.Ldarg_S, (sbyte)(i + 1));
+
+ IL.Emit(OpCodes.Call, method);
+ IL.Emit(OpCodes.Ret);
IL.MarkLabel(skipThrow);
// Push the 'this' pointer onto the stack
View
40 src/NHibernate/Proxy/DynamicProxy/DefaultProxyAssemblyBuilder.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace NHibernate.Proxy.DynamicProxy
+{
+ public class DefaultProxyAssemblyBuilder : IProxyAssemblyBuilder
+ {
+ private AssemblyBuilder assemblyBuilder;
+
+ public AssemblyBuilder DefineDynamicAssembly(AppDomain appDomain, AssemblyName name)
+ {
+#if DEBUG
+ AssemblyBuilderAccess access = AssemblyBuilderAccess.RunAndSave;
+#else
+ AssemblyBuilderAccess access = AssemblyBuilderAccess.Run;
+#endif
+ assemblyBuilder = appDomain.DefineDynamicAssembly(name, access);
@oskarb
NHibernate member
oskarb added a note Jun 2, 2012

It seems the ProxyAssemblyBuilder can potentially be called concurrently from different threads since the ProxyFactory doesn't guard against this. Which makes keeping the assembly build in instance state would be problematic since it could be replaced before DefineDynamicModule() gets called.

On the other hand, the ProxyFactory seems to have bigger problems with respect to thread safety, see NH-3172.

@oskarb
NHibernate member
oskarb added a note Jun 2, 2012

Actually, it is precisely this commit which causes NH-3172. But lack of locking in PRoxyFactory.CreateProxyType() may cause the same proxy type to be generated multiple times, though the Cache implementation will simply discard all but the last. I'm looking into it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ return assemblyBuilder;
+ }
+
+ public ModuleBuilder DefineDynamicModule(string moduleName)
+ {
+#if DEBUG
+ ModuleBuilder moduleBuilder =
+ assemblyBuilder.DefineDynamicModule(moduleName, string.Format("{0}.mod", moduleName), true);
+#else
+ ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(moduleName);
+#endif
+ return moduleBuilder;
+ }
+
+ public void Save()
+ {
+#if DEBUG_PROXY_OUTPUT
+ assemblyBuilder.Save("generatedAssembly.dll");
+#endif
+ }
+ }
+}
View
13 src/NHibernate/Proxy/DynamicProxy/IProxyAssemblyBuilder.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace NHibernate.Proxy.DynamicProxy
+{
+ public interface IProxyAssemblyBuilder
+ {
+ AssemblyBuilder DefineDynamicAssembly(AppDomain appDomain, AssemblyName name);
+ ModuleBuilder DefineDynamicModule(string moduleName);
+ void Save();
+ }
+}
View
41 src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs
@@ -17,7 +17,7 @@ namespace NHibernate.Proxy.DynamicProxy
{
public sealed class ProxyFactory
{
- private static readonly ConstructorInfo baseConstructor = typeof(object).GetConstructor(new System.Type[0]);
+ private static readonly ConstructorInfo defaultBaseConstructor = typeof(object).GetConstructor(new System.Type[0]);
private static readonly MethodInfo getTypeFromHandle = typeof(System.Type).GetMethod("GetTypeFromHandle");
private static readonly MethodInfo getValue = typeof (SerializationInfo).GetMethod("GetValue", BindingFlags.Public | BindingFlags.Instance, null,
@@ -31,20 +31,29 @@ public sealed class ProxyFactory
public ProxyFactory()
: this(new DefaultyProxyMethodBuilder()) {}
+ public ProxyFactory(IProxyAssemblyBuilder proxyAssemblyBuilder)
+ : this(new DefaultyProxyMethodBuilder(), proxyAssemblyBuilder) {}
+
public ProxyFactory(IProxyMethodBuilder proxyMethodBuilder)
+ : this(new DefaultyProxyMethodBuilder(), new DefaultProxyAssemblyBuilder()) {}
+
+ public ProxyFactory(IProxyMethodBuilder proxyMethodBuilder, IProxyAssemblyBuilder proxyAssemblyBuilder)
{
if (proxyMethodBuilder == null)
{
throw new ArgumentNullException("proxyMethodBuilder");
}
ProxyMethodBuilder = proxyMethodBuilder;
+ ProxyAssemblyBuilder = proxyAssemblyBuilder;
Cache = new ProxyCache();
}
public IProxyCache Cache { get; private set; }
public IProxyMethodBuilder ProxyMethodBuilder { get; private set; }
+ public IProxyAssemblyBuilder ProxyAssemblyBuilder { get; private set; }
+
public object CreateProxy(System.Type instanceType, IInterceptor interceptor, params System.Type[] baseInterfaces)
{
System.Type proxyType = CreateProxyType(instanceType, baseInterfaces);
@@ -83,19 +92,8 @@ private System.Type CreateUncachedProxyType(System.Type baseType, System.Type[]
string moduleName = string.Format("{0}Module", typeName);
var name = new AssemblyName(assemblyName);
-#if DEBUG
- AssemblyBuilderAccess access = AssemblyBuilderAccess.RunAndSave;
-#else
- AssemblyBuilderAccess access = AssemblyBuilderAccess.Run;
-#endif
- AssemblyBuilder assemblyBuilder = currentDomain.DefineDynamicAssembly(name, access);
-
-#if DEBUG
- ModuleBuilder moduleBuilder =
- assemblyBuilder.DefineDynamicModule(moduleName, string.Format("{0}.mod", moduleName), true);
-#else
- ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(moduleName);
-#endif
+ AssemblyBuilder assemblyBuilder = ProxyAssemblyBuilder.DefineDynamicAssembly(currentDomain, name);
+ ModuleBuilder moduleBuilder = ProxyAssemblyBuilder.DefineDynamicModule(moduleName);
TypeAttributes typeAttributes = TypeAttributes.AutoClass | TypeAttributes.Class |
TypeAttributes.Public | TypeAttributes.BeforeFieldInit;
@@ -124,7 +122,7 @@ private System.Type CreateUncachedProxyType(System.Type baseType, System.Type[]
TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, typeAttributes, parentType, interfaces.ToArray());
- ConstructorBuilder defaultConstructor = DefineConstructor(typeBuilder);
+ ConstructorBuilder defaultConstructor = DefineConstructor(typeBuilder, parentType);
// Implement IProxy
var implementor = new ProxyImplementor();
@@ -143,9 +141,7 @@ private System.Type CreateUncachedProxyType(System.Type baseType, System.Type[]
AddSerializationSupport(baseType, baseInterfaces, typeBuilder, interceptorField, defaultConstructor);
System.Type proxyType = typeBuilder.CreateType();
-#if DEBUG_PROXY_OUTPUT
- assemblyBuilder.Save("generatedAssembly.dll");
-#endif
+ ProxyAssemblyBuilder.Save();
return proxyType;
}
@@ -177,7 +173,7 @@ private IEnumerable<MethodInfo> GetProxiableMethods(System.Type type, IEnumerabl
.Concat(interfaces.SelectMany(interfaceType => interfaceType.GetMethods())).Distinct();
}
- private static ConstructorBuilder DefineConstructor(TypeBuilder typeBuilder)
+ private static ConstructorBuilder DefineConstructor(TypeBuilder typeBuilder, System.Type parentType)
{
const MethodAttributes constructorAttributes = MethodAttributes.Public |
MethodAttributes.HideBySig | MethodAttributes.SpecialName |
@@ -186,6 +182,13 @@ private static ConstructorBuilder DefineConstructor(TypeBuilder typeBuilder)
ConstructorBuilder constructor =
typeBuilder.DefineConstructor(constructorAttributes, CallingConventions.Standard, new System.Type[0]);
+ var baseConstructor = parentType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, new System.Type[0], null);
+
+ // if there is no default constructor, or the default constructor is private/internal, call System.Object constructor
+ // this works, but the generated assembly will fail PeVerify (cannot use in medium trust for example)
+ if (baseConstructor == null || baseConstructor.IsPrivate || baseConstructor.IsAssembly)
+ baseConstructor = defaultBaseConstructor;
+
ILGenerator IL = constructor.GetILGenerator();
constructor.SetImplementationFlags(MethodImplAttributes.IL | MethodImplAttributes.Managed);

0 comments on commit d259996

Please sign in to comment.
Something went wrong with that request. Please try again.