Merge pull request #4816 from BrzVlad/fix-remoting-exception
authorVlad Brezae <brezaevlad@gmail.com>
Wed, 10 May 2017 22:38:33 +0000 (01:38 +0300)
committerGitHub <noreply@github.com>
Wed, 10 May 2017 22:38:33 +0000 (01:38 +0300)
[remoting] Serialize exceptions between domains inside try/catch

mono/metadata/remoting.c
mono/tests/Makefile.am
mono/tests/appdomain-serialize-exception.cs [new file with mode: 0644]

index 9578b47e5d5ec61ae5ff9d8b2be8574fc485535d..ea2ff0c3d7b2fc029d42a882f828d220bee6258f 100644 (file)
@@ -671,8 +671,8 @@ mono_marshal_get_xappdomain_dispatch (MonoMethod *method, int *marshal_types, in
        int i, j, param_index, copy_locals_base;
        MonoClass *ret_class = NULL;
        int loc_array=0, loc_return=0, loc_serialized_exc=0;
-       MonoExceptionClause *main_clause;
-       int pos, pos_leave;
+       MonoExceptionClause *clauses, *main_clause, *serialization_clause;
+       int pos, pos_leave, pos_leave_serialization;
        gboolean copy_return;
        WrapperInfo *info;
 
@@ -714,7 +714,8 @@ mono_marshal_get_xappdomain_dispatch (MonoMethod *method, int *marshal_types, in
 
        /* try */
 
-       main_clause = (MonoExceptionClause *)mono_image_alloc0 (method->klass->image, sizeof (MonoExceptionClause));
+       clauses = (MonoExceptionClause *)mono_image_alloc0 (method->klass->image, 2 * sizeof (MonoExceptionClause));
+       main_clause = &clauses [0];
        main_clause->try_offset = mono_mb_get_label (mb);
 
        /* Clean the call context */
@@ -894,15 +895,48 @@ mono_marshal_get_xappdomain_dispatch (MonoMethod *method, int *marshal_types, in
        
        /* handler code */
        main_clause->handler_offset = mono_mb_get_label (mb);
+
+       /*
+        * We deserialize the exception in another try-catch so we can catch
+        * serialization failure exceptions.
+        */
+       serialization_clause = &clauses [1];
+       serialization_clause->try_offset = mono_mb_get_label (mb);
+
+       mono_mb_emit_managed_call (mb, method_rs_serialize_exc, NULL);
+       mono_mb_emit_stloc (mb, loc_serialized_exc);
+       mono_mb_emit_ldarg (mb, 2);
+       mono_mb_emit_ldloc (mb, loc_serialized_exc);
+       mono_mb_emit_byte (mb, CEE_STIND_REF);
+       pos_leave_serialization = mono_mb_emit_branch (mb, CEE_LEAVE);
+
+       /* Serialization exception catch */
+       serialization_clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
+       serialization_clause->try_len = mono_mb_get_pos (mb) - serialization_clause->try_offset;
+       serialization_clause->data.catch_class = mono_defaults.object_class;
+
+       /* handler code */
+       serialization_clause->handler_offset = mono_mb_get_label (mb);
+
+       /*
+        * If the serialization of the original exception failed we serialize the newly
+        * thrown exception, which should always succeed, passing it over to the calling
+        * domain.
+        */
        mono_mb_emit_managed_call (mb, method_rs_serialize_exc, NULL);
        mono_mb_emit_stloc (mb, loc_serialized_exc);
        mono_mb_emit_ldarg (mb, 2);
        mono_mb_emit_ldloc (mb, loc_serialized_exc);
        mono_mb_emit_byte (mb, CEE_STIND_REF);
        mono_mb_emit_branch (mb, CEE_LEAVE);
-       main_clause->handler_len = mono_mb_get_pos (mb) - main_clause->handler_offset;
-       /* end catch */
 
+       /* end serialization exception catch */
+       serialization_clause->handler_len = mono_mb_get_pos (mb) - serialization_clause->handler_offset;
+       mono_mb_patch_branch (mb, pos_leave_serialization);
+
+       mono_mb_emit_branch (mb, CEE_LEAVE);
+       /* end main catch */
+       main_clause->handler_len = mono_mb_get_pos (mb) - main_clause->handler_offset;
        mono_mb_patch_branch (mb, pos_leave);
        
        if (copy_return)
@@ -910,7 +944,7 @@ mono_marshal_get_xappdomain_dispatch (MonoMethod *method, int *marshal_types, in
 
        mono_mb_emit_byte (mb, CEE_RET);
 
-       mono_mb_set_clauses (mb, 1, main_clause);
+       mono_mb_set_clauses (mb, 2, clauses);
 #endif
 
        info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
index f8946b03be7cbd909b4c31d8b448741bcf8a01b3..fc1259252f3d9750bb61c8073b87f69f36a433dd 100644 (file)
@@ -458,6 +458,7 @@ TESTS_CS_SRC=               \
        appdomain1.cs   \
        appdomain2.cs   \
        appdomain-exit.cs       \
+       appdomain-serialize-exception.cs \
        assemblyresolve_event2.2.cs     \
        appdomain-unload-callback.cs    \
        appdomain-unload-doesnot-raise-pending-events.cs        \
diff --git a/mono/tests/appdomain-serialize-exception.cs b/mono/tests/appdomain-serialize-exception.cs
new file mode 100644 (file)
index 0000000..8b0fe47
--- /dev/null
@@ -0,0 +1,45 @@
+using System;
+using System.Reflection;
+using System.Runtime.Serialization;
+
+public class UnserializableException : Exception
+{
+}
+
+public class TestOutput : MarshalByRefObject
+{
+       public void ThrowUnserializable ()
+       {
+               Console.WriteLine("Throwing Unserializable exception in AppDomain \"{0}\"", AppDomain.CurrentDomain.FriendlyName);
+               throw new UnserializableException ();
+       }
+}
+
+public class Example
+{
+       public static int Main ()
+       {
+               string original_domain = AppDomain.CurrentDomain.FriendlyName;
+
+               AppDomain ad = AppDomain.CreateDomain("subdomain");
+               try {
+                       TestOutput remoteOutput = (TestOutput) ad.CreateInstanceAndUnwrap(
+                               typeof (TestOutput).Assembly.FullName,
+                               "TestOutput");
+                       remoteOutput.ThrowUnserializable ();
+               } catch (SerializationException) {
+                       Console.WriteLine ("Caught serialization exception");
+               } catch (Exception) {
+                       Console.WriteLine ("Caught other exception");
+                       Environment.Exit (1);
+               } finally {
+                       Console.WriteLine ("Finally in domain {0}", AppDomain.CurrentDomain.FriendlyName);
+                       if (original_domain != AppDomain.CurrentDomain.FriendlyName)
+                               Environment.Exit (2);
+                       AppDomain.Unload (ad);
+               }
+
+               Console.WriteLine ("All OK");
+               return 0;
+       }
+}