[corlib] Fixed StringBuilder construction bugs in marshalling caused by changes to...
[mono.git] / mcs / class / corlib / System.Runtime.Serialization.Formatters.Binary / BinaryFormatter.cs
index 8a729c2a72bd74086e707e779699c87aa5a20073..4d4756e150d5a16689db6d7109feaaad950c38eb 100644 (file)
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System.Reflection;
 using System.Collections;
 using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
 using System.Runtime.Remoting.Messaging;
 using System.Security.Permissions;
 
 namespace System.Runtime.Serialization.Formatters.Binary {
 
-       public sealed class BinaryFormatter : IRemotingFormatter, IFormatter 
+       [ComVisible (true)]
+       public sealed class BinaryFormatter :
+#if !FULL_AOT_RUNTIME
+                                               IRemotingFormatter,
+#endif
+                                               IFormatter
        {
-               private FormatterAssemblyStyle assembly_format = FormatterAssemblyStyle.Full;
+               private FormatterAssemblyStyle assembly_format = FormatterAssemblyStyle.Simple;
                private SerializationBinder binder;
                private StreamingContext context;
                private ISurrogateSelector surrogate_selector;
                private FormatterTypeStyle type_format = FormatterTypeStyle.TypesAlways;
-               
-#if NET_1_1
-               private TypeFilterLevel filter_level;
-#endif
+               private TypeFilterLevel filter_level = TypeFilterLevel.Full;
                
                public BinaryFormatter()
                {
-                       surrogate_selector=null;
+                       surrogate_selector=DefaultSurrogateSelector;
                        context=new StreamingContext(StreamingContextStates.All);
                }
                
@@ -58,7 +61,17 @@ namespace System.Runtime.Serialization.Formatters.Binary {
                        surrogate_selector=selector;
                        this.context=context;
                }
+               
 
+               // Deserializing objects of type Dictionary<,> List<> and friends does not work in a CoreCLR sandbox, because
+               // the default deserialization code uses reflection to do its job, and the fields being reflected on live in mscorlib.dll.
+               // DefaultSurrogateSelector enables embedders to provide an alternative method of deserializing specific types in a way
+               // that does not violate the CoreCLR rules. See https://gist.github.com/878267 for some actual code that provides CoreCLR safe 
+               // deserialization code for List<> and Dictionary<,>.
+               // DefaultSurrogateSelector is private, and needs to be set by the embedder trough reflection, so we do not expose any public
+               // API point that is not present in .NET
+               static ISurrogateSelector DefaultSurrogateSelector { get; set; }
+               
                public FormatterAssemblyStyle AssemblyFormat
                {
                        get {
@@ -109,28 +122,33 @@ namespace System.Runtime.Serialization.Formatters.Binary {
                        }
                }
 
-#if NET_1_1
-               [System.Runtime.InteropServices.ComVisible (false)]
                public TypeFilterLevel FilterLevel 
                {
                        get { return filter_level; }
                        set { filter_level = value; }
                }
-#endif
 
-               public object Deserialize(Stream serializationStream)
+               [SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)]
+               public object Deserialize (Stream serializationStream)
                {
-                       return Deserialize (serializationStream, null);
+                       return NoCheckDeserialize (serializationStream, null);
                }
 
-               public object Deserialize(Stream serializationStream, HeaderHandler handler) 
+               [SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)]
+               public object Deserialize (Stream serializationStream, HeaderHandler handler) 
                {
-                       if(serializationStream==null) \r
+                       return NoCheckDeserialize (serializationStream, handler);
+               }
+
+               // shared by Deserialize and UnsafeDeserialize which both involve different security checks
+               private object NoCheckDeserialize (Stream serializationStream, HeaderHandler handler)
+               {
+                       if(serializationStream==null) 
                        {
-                               throw new ArgumentNullException("serializationStream is null");
+                               throw new ArgumentNullException("serializationStream");
                        }
                        if(serializationStream.CanSeek &&
-                               serializationStream.Length==0) \r
+                               serializationStream.Length==0) 
                        {
                                throw new SerializationException("serializationStream supports seeking, but its length is 0");
                        }
@@ -143,29 +161,36 @@ namespace System.Runtime.Serialization.Formatters.Binary {
                        // Messages are read using a special static method, which does not use ObjectReader
                        // if it is not needed. This saves time and memory.
 
-                       BinaryElement elem = (BinaryElement) reader.PeekChar();
+                       BinaryElement elem = (BinaryElement) reader.Read ();
 
                        if (elem == BinaryElement.MethodCall) {
-                               return MessageFormatter.ReadMethodCall (reader, hasHeader, handler, this);
+                               return MessageFormatter.ReadMethodCall (elem, reader, hasHeader, handler, this);
                        }
                        else if (elem == BinaryElement.MethodResponse) {
-                               return MessageFormatter.ReadMethodResponse (reader, hasHeader, handler, null, this);
+                               return MessageFormatter.ReadMethodResponse (elem, reader, hasHeader, handler, null, this);
                        }
                        else {
                                ObjectReader serializer = new ObjectReader (this);
 
                                object result;
                                Header[] headers;
-                               serializer.ReadObjectGraph (reader, hasHeader, out result, out headers);
+                               serializer.ReadObjectGraph (elem, reader, hasHeader, out result, out headers);
                                if (handler != null) handler(headers);
                                return result;
                        }
                }
                
-               public object DeserializeMethodResponse(Stream serializationStream, HeaderHandler handler, IMethodCallMessage methodCallmessage)
+               [SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)]
+               public object DeserializeMethodResponse (Stream serializationStream, HeaderHandler handler, IMethodCallMessage methodCallMessage)
+               {
+                       return NoCheckDeserializeMethodResponse (serializationStream, handler, methodCallMessage);
+               }
+
+               // shared by DeserializeMethodResponse and UnsafeDeserializeMethodResponse which both involve different security checks
+               private object NoCheckDeserializeMethodResponse (Stream serializationStream, HeaderHandler handler, IMethodCallMessage methodCallMessage)
                {
                        if(serializationStream==null) {
-                               throw new ArgumentNullException("serializationStream is null");
+                               throw new ArgumentNullException("serializationStream");
                        }
                        if(serializationStream.CanSeek &&
                           serializationStream.Length==0) {
@@ -176,7 +201,7 @@ namespace System.Runtime.Serialization.Formatters.Binary {
 
                        bool hasHeader;
                        ReadBinaryHeader (reader, out hasHeader);
-                       return MessageFormatter.ReadMethodResponse (reader, hasHeader, handler, methodCallmessage, this);
+                       return MessageFormatter.ReadMethodResponse (reader, hasHeader, handler, methodCallMessage, this);
                }
 
                public void Serialize(Stream serializationStream, object graph)
@@ -192,53 +217,57 @@ namespace System.Runtime.Serialization.Formatters.Binary {
                        }
 
                        BinaryWriter writer = new BinaryWriter (serializationStream);
-                       WriteBinaryHeader (writer, headers!=null);\r
-\r
-                       if (graph is IMethodCallMessage) {\r
-                               MessageFormatter.WriteMethodCall (writer, graph, headers, surrogate_selector, context, assembly_format, type_format);\r
-                       }\r
-                       else if (graph is IMethodReturnMessage)  {\r
-                               MessageFormatter.WriteMethodResponse (writer, graph, headers, surrogate_selector, context, assembly_format, type_format);\r
-                       }\r
-                       else {\r
-                               ObjectWriter serializer = new ObjectWriter (surrogate_selector, context, assembly_format, type_format);
+                       WriteBinaryHeader (writer, headers!=null);
+
+                       if (graph is IMethodCallMessage) {
+                               MessageFormatter.WriteMethodCall (writer, graph, headers, this);
+                       }
+                       else if (graph is IMethodReturnMessage)  {
+                               MessageFormatter.WriteMethodResponse (writer, graph, headers, this);
+                       }
+                       else {
+                               ObjectWriter serializer = new ObjectWriter (this);
                                serializer.WriteObjectGraph (writer, graph, headers);
                        }
                        writer.Flush();
                }
-\r
-               [MonoTODO]
-               [System.Runtime.InteropServices.ComVisible (false)]
-               public object UnsafeDeserialize(Stream serializationStream, HeaderHandler handler) 
+
+               // faster version (under CAS) as this requires a LinkDemand versus full Demand (i.e. a stack-walk)
+               // shouldn't be called unless the code is intended to be executed at full-trust
+               [ComVisible (false)]
+               [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)]
+               public object UnsafeDeserialize (Stream serializationStream, HeaderHandler handler) 
                {
-                       throw new NotImplementedException ();
+                       return NoCheckDeserialize (serializationStream, handler);
                }
                
-               [MonoTODO]
-               [System.Runtime.InteropServices.ComVisible (false)]
-               public object UnsafeDeserializeMethodResponse(Stream serializationStream, HeaderHandler handler, IMethodCallMessage methodCallmessage)
+               // faster version (under CAS) as this requires a LinkDemand versus full Demand (i.e. a stack-walk)
+               // shouldn't be called unless the code is intended to be executed at full-trust
+               [ComVisible (false)]
+               [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)]
+               public object UnsafeDeserializeMethodResponse (Stream serializationStream, HeaderHandler handler, IMethodCallMessage methodCallMessage)
                {
-                       throw new NotImplementedException ();
+                       return NoCheckDeserializeMethodResponse (serializationStream, handler, methodCallMessage);
                }
                
-               private void WriteBinaryHeader (BinaryWriter writer, bool hasHeaders)\r
-               {\r
-                       writer.Write ((byte)BinaryElement.Header);\r
-                       writer.Write ((int)1);\r
-                       if (hasHeaders) writer.Write ((int)2);\r
-                       else writer.Write ((int)-1);\r
-                       writer.Write ((int)1);\r
-                       writer.Write ((int)0);\r
-               }\r
-\r
-               private void ReadBinaryHeader (BinaryReader reader, out bool hasHeaders)\r
-               {\r
-                       reader.ReadByte();\r
-                       reader.ReadInt32();\r
-                       int val = reader.ReadInt32();\r
-                       hasHeaders = (val==2);\r
-                       reader.ReadInt32();\r
-                       reader.ReadInt32();\r
-               }\r
+               private void WriteBinaryHeader (BinaryWriter writer, bool hasHeaders)
+               {
+                       writer.Write ((byte)BinaryElement.Header);
+                       writer.Write ((int)1);
+                       if (hasHeaders) writer.Write ((int)2);
+                       else writer.Write ((int)-1);
+                       writer.Write ((int)1);
+                       writer.Write ((int)0);
+               }
+
+               private void ReadBinaryHeader (BinaryReader reader, out bool hasHeaders)
+               {
+                       reader.ReadByte();
+                       reader.ReadInt32();
+                       int val = reader.ReadInt32();
+                       hasHeaders = (val==2);
+                       reader.ReadInt32();
+                       reader.ReadInt32();
+               }
        }
 }