[remoting] transparent proxy GetType of an interface should return the
authorAleksey Kliger <aleksey@xamarin.com>
Wed, 9 Aug 2017 22:38:50 +0000 (18:38 -0400)
committerAleksey Kliger (λgeek) <akliger@gmail.com>
Mon, 14 Aug 2017 19:38:53 +0000 (15:38 -0400)
interface Type, not MarshalByRefObject. (Fixes #17325)

If a RealProxy instance is created (using the protected RealProxy(Type)
constructor) for an interface, mono internally sets up a MonoRemoteClass which
looks like a proxy for a MarshalByRefObject instance that happens to implement
the given interface.  However mono_class_proxy_vtable just does a memcpy() to
setup the MonoVTable:type field of the transparent proxy's vtable.  That type
field is hit by the JIT when System.Object.GetType() is called - and on .NET
Framework GetType for a transparent proxy of an interface returns the interface
not (as Mono used to) typeof(MarshalByRefObject).

Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=17325

mono/metadata/icall.c
mono/metadata/object-internals.h
mono/metadata/object.c

index 76ccc633aacb095aa61157cda783347c1df962ee..2e485848486d0363b3b7d0929849fcec894b26b6 100644 (file)
@@ -1256,7 +1256,13 @@ ves_icall_System_Object_GetType (MonoObjectHandle obj, MonoError *error)
        if (mono_class_is_transparent_proxy (klass)) {
                MonoTransparentProxyHandle proxy_obj = MONO_HANDLE_CAST (MonoTransparentProxy, obj);
                MonoRemoteClass *remote_class = MONO_HANDLE_GETVAL (proxy_obj, remote_class);
-               MonoType *proxy_type = &remote_class->proxy_class->byval_arg;
+               /* If it's a transparent proxy for an interface, return the
+                * interface type, not the unhelpful proxy_class class (which
+                * is just MarshalByRefObject). */
+               MonoType *proxy_type =
+                       mono_remote_class_is_interface_proxy (remote_class) ?
+                       &remote_class->interfaces[0]->byval_arg :
+                       &remote_class->proxy_class->byval_arg;
                return mono_type_get_object_handle (domain, proxy_type, error);
        } else
 #endif
index 6167cb0ca127efd96d6f3cfb66ca2d3e058c4e8f..62c8df717de03de5d56228b9a3708350ba93d163 100644 (file)
@@ -1571,6 +1571,9 @@ ves_icall_array_new_specific (MonoVTable *vtable, uintptr_t n);
 MonoRemoteClass*
 mono_remote_class (MonoDomain *domain, MonoStringHandle class_name, MonoClass *proxy_class, MonoError *error);
 
+gboolean
+mono_remote_class_is_interface_proxy (MonoRemoteClass *remote_class);
+
 MonoObject *
 mono_remoting_invoke (MonoObject *real_proxy, MonoMethodMessage *msg, MonoObject **exc, MonoArray **out_args, MonoError *error);
 
index 45f261697b6243399776adc012750e770b441291..6afa95de3981b09ab0df3e4aaad406b5f20791c9 100644 (file)
@@ -2155,6 +2155,22 @@ mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *klass, MonoErro
 }
 
 #ifndef DISABLE_REMOTING
+/**
+ * mono_remote_class_is_interface_proxy:
+ * \param remote_class
+ *
+ * Returns TRUE if the given remote class is a proxying an interface (as
+ * opposed to a class deriving from MarshalByRefObject).
+ */
+gboolean
+mono_remote_class_is_interface_proxy (MonoRemoteClass *remote_class)
+{
+       /* This if condition is taking advantage of how mono_remote_class ()
+        * works: if that code changes, this needs to change too. */
+       return (remote_class->interface_count >= 1 &&
+               remote_class->proxy_class == mono_defaults.marshalbyrefobject_class);
+}
+
 /**
  * mono_class_proxy_vtable:
  * \param domain the application domain
@@ -2249,6 +2265,18 @@ mono_class_proxy_vtable (MonoDomain *domain, MonoRemoteClass *remote_class, Mono
        /* we need to keep the GC descriptor for a transparent proxy or we confuse the precise GC */
        pvt->gc_descr = mono_defaults.transparent_proxy_class->gc_descr;
 
+       if (mono_remote_class_is_interface_proxy (remote_class)) {
+               /* If it's a transparent proxy for an interface, set the
+                * MonoVTable:type to the interface type, not the placeholder
+                * MarshalByRefObject class.  This is used when mini JITs calls
+                * to Object.GetType ()
+                */
+               MonoType *itf_proxy_type = &remote_class->interfaces[0]->byval_arg;
+               pvt->type = mono_type_get_object_checked (domain, itf_proxy_type, error);
+               if (!is_ok (error))
+                       goto failure;
+       }
+
        /* initialize vtable */
        mono_class_setup_vtable (klass);
        for (i = 0; i < klass->vtable_size; ++i) {
@@ -2516,6 +2544,12 @@ mono_remote_class (MonoDomain *domain, MonoStringHandle class_name, MonoClass *p
        key = mp_key;
 
        if (mono_class_is_interface (proxy_class)) {
+               /* If we need to proxy an interface, we use this stylized
+                * representation (interface_count >= 1, proxy_class is
+                * MarshalByRefObject).  The code in
+                * mono_remote_class_is_interface_proxy () depends on being
+                * able to detect that we're doing this, so if this
+                * representation changes, change GetType, too. */
                rc = (MonoRemoteClass *)mono_domain_alloc (domain, MONO_SIZEOF_REMOTE_CLASS + sizeof(MonoClass*));
                rc->interface_count = 1;
                rc->interfaces [0] = proxy_class;