I can see right through you

Once communication can happen across the boundary between a virtual machine and the code running on top of it, things can get a little interesting. In particular, behaviour that seems obvious may not happen. This blog entry shows a short journey in the quest to get the following piece of code to fail.
 

object obj = new MyObject();
MyObject obj2 = (MyObject)obj;

We start with MyObject having the definition

class MyObject {}


Now it turns out that the .NET framework has support for an object called a Transparent proxy. This special type, which is known to the virtual machine, has the ability to turn standard function calls into a objects supporting the IMessage interface. This interface allows the message object to be queried for details about the target of the call, the actual method and the arguments. There is additional support for returning a result from the trapped call. Of course, this interception doesn’t come for free, requiring the VM to disable various optimisations. This is done on a per-object basis by making the object inherit from MarshalByRefObject. The virtual machine can then test to see if an object inherits from this class in order to see if various optimisations are allowed.

The first thing we need to do is to hijack the object creation process, allowing us to put the transparent proxy between the newly minted object and the client. For ContextBound objects, where ContextBound is a subclass of MarshalByRefObject, the CLR will look for attributes of a certain type during the object creation process and use the CreateInstance method on these attributes to form the target object. We define a suitable attribute for inserting our proxy into the creation process.

[

AttributeUsage(AttributeTargets.Class]
class MyProxyAttribute : ProxyAttribute
{
 
public override MarshalByRefObject CreateInstance(Type serverType)
  {
   
MarshalByRefObject target = base.CreateInstance(serverType);
   
RealProxy proxy = new MyProxy(target, serverType);
   
return (MarshalByRefObject)proxy.GetTransparentProxy();
  }
}

Our proxy is a subclass of RealProxy. It will override the Invoke method. This is the method that is passed an object reflecting the call that is being made. We need to handle the case where we are being called in the construction process by checking for IConstructionCallMessage in which case we can use InitializeServerObject to allow the construction of the object to complete. Typically in the non-constructor case we would use something like RemotingServices.ExecuteMessage to convert the object representation of the call into a normal C# call. Here we simply assume that the method will return a string and return a string containing the name of the called method.

Normally, the proxy would not implement IRemotingTypeInfo for which we define CanCastTo and TypeName items that throw an exception, but we’ll use these later.

class MyProxy : RealProxy, IRemotingTypeInfo
{
 
readonly MarshalByRefObject m_Target;
 
public MyProxy(MarshalByRefObject target, Type serverType) : base(serverType)
  {
    m_Target = target;
  }
 
public override System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg)
  {
   
IMethodCallMessage call = (IMethodCallMessage)msg;
   
IConstructionCallMessage ctor = call as IConstructionCallMessage;
   
if (ctor != null)
    {
     
RealProxy rp = RemotingServices.GetRealProxy(m_Target);
      rp.InitializeServerObject(ctor);
     
MarshalByRefObject tp = (MarshalByRefObject)this.GetTransparentProxy();
     
return EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor, tp);
    }
   
string result = "This was a call to " + call.MethodName;
   
return new ReturnMessage(result, null, 0, null, call);
  }
 
public bool CanCastTo(Type fromType, object o)
  {
   
throw new NotImplementedException();
  }
 
public string TypeName
  {
   
get
   
{
     
throw new NotImplementedException();
    }
   
set
   
{
     
throw new NotImplementedException();
    }
  }
}

We add a method to MyObject to demonstrate this.

 [

MyProxy]
class MyObject : ContextBoundObject
{
  public
string Message1()
  {
   
return "Hello";
  }

}

Calling the following code will now leave result1 with the value "This was a call to Message1"

object obj = new MyObject();
MyObject obj2 = (MyObject)obj;
string result1 = obj2.Message1();


The CanCastTo method is used by the VM to determine the result of type tests. If we add a new interface

interface IAnother
{
 
string Message2();
}


and change the definition of CanCastTo to

public bool CanCastTo(Type fromType, object o)
{
 
return true;
}

 
then we can also run the code, even though MyObject does not implement this interface, leaving result2 equal to "This was a call to Message2".

IAnother obj3 = obj2 as IAnother;
string result2 = obj3.Message2();

 

If we now define a new class

class Dummy : MarshalByRefObject {}

and redefine the proxy to prevent the casting in CanCastTo and to make this type the proxy target type

class MyProxy : RealProxy, IRemotingTypeInfo
{
 
public MyProxy(MarshalByRefObject target, Type serverType) : base(typeof(Dummy))
  {
  }
 
public override System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg)
  {
   
IMethodCallMessage call = (IMethodCallMessage)msg;
   
IConstructionCallMessage ctor = call as IConstructionCallMessage;
   
if (ctor != null)
    {
     
MarshalByRefObject tp = (MarshalByRefObject)this.GetTransparentProxy();
     
return EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor, tp);
    }
   
string result = "This was a call to " + call.MethodName;
   
return new ReturnMessage(result, null, 0, null, call);
  }
 
public bool CanCastTo(Type fromType, object o)
  {
   
return false;
  }
 
public string TypeName
  {
   
get
    
{
     
throw new NotImplementedException();
    }
    
set
    
{
     
throw new NotImplementedException();
    }
  }
}

 

then running this code causes an InvalidCastException on the second line.

object obj = new MyObject();
MyObject
obj2 = (MyObject)obj;
 
 

What has this shown? Simply that Transparent proxies live in the region between the virtual machine and the normal world of code that compiles to IL, and that code running in this region can generate strange behaviours. 

Advertisements
This entry was posted in Computers and Internet. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s