[Mono-bugs] [Bug 80332][Wis] New - info.AddValue(String, Object, Type) causes corrupt Binary Serialization

bugzilla-daemon at bugzilla.ximian.com bugzilla-daemon at bugzilla.ximian.com
Wed Dec 20 15:33:35 EST 2006


Please do not reply to this email- if you want to comment on the bug, go to the
URL shown below and enter your comments there.

Changed by andyhume32 at yahoo.co.uk.

http://bugzilla.ximian.com/show_bug.cgi?id=80332

--- shadow/80332	2006-12-20 15:33:35.000000000 -0500
+++ shadow/80332.tmp.15774	2006-12-20 15:33:35.000000000 -0500
@@ -0,0 +1,179 @@
+Bug#: 80332
+Product: Mono: Class Libraries
+Version: 1.2
+OS: 
+OS Details: 
+Status: NEW   
+Resolution: 
+Severity: 
+Priority: Wishlist
+Component: CORLIB
+AssignedTo: mono-bugs at ximian.com                            
+ReportedBy: andyhume32 at yahoo.co.uk               
+QAContact: mono-bugs at ximian.com
+TargetMilestone: ---
+URL: 
+Cc: 
+Summary: info.AddValue(String, Object,Type) causes corrupt Binary Serialization
+
+I have an existing library which does the following.
+
+And is more completely:
+
+class MySerializableClass : ISerializable {
+        // Note we store the enum value as an Int32, just in case
+        // it was ever to change format...
+        public void GetObjectData(SerializationInfo info, 
+StreamingContext context)
+        {
+            // *** This is line the causes the problem ***
+            // m_fooCode is "enum FooByteEnum : byte { ... }"
+            info.AddValue("FooCode", this.m_fooCode, typeof(Int32));
+        }
+
+        protected DummySerializableClass(SerializationInfo info, 
+StreamingContext context)
+        {
+            this.m_fooCode = (FooByteEnum)info.GetInt32("FooCode");
+        }
+}
+(Where m_fooCode is "enum FooByteEnum : byte { ... }").
+
+Now in hindsight I agree that the use of the AddValue method is a bit 
+odd, perhaps with an explicit cast, and also just using AddValue
+(String,Int32) directly, being better...
+
+However with binary serialization on the MSFT CLR it works, but when I 
+run the library's unit-tests on the Mono CLR it fails at deserialization 
+time with: "System.Runtime.Serialization.SerializationException : 
+Unexpected binary element: 0".  Further testing shows that there is also 
+an error when the MSFT CLR too is used to read the Mono output 
+("System.Runtime.Serialization.SerializationException: Binary  stream '0' 
+does not contain a valid BinaryHeader. Possible causes are invalid stream 
+or object version change between serialization and deserialization.").
+
+Manual decoding of the binary output shows that the MSFT version writes 
+{type=Int32 value=xx-xx-xx-xx}, whereas Mono writes {type=Int32 
+value=FooByteEnum=xx}.  Not too surprising the deserialization fails. :-,
+( 
+
+My full decoding of the output created by the initial example is as 
+follows.  This is by the use of the nice document, 
+http://primates.ximian.com/~lluis/dist/binary_serialization_format.htm
+
+* MSFT 2.0.50727.42.bin
+Header
+00 01 00 00 00 FF FF FF  FF 01 00 00 00 00 00 00 00 Assembly
+0C, 02 00 00 00     ID=2
+43,
+4D 6F 6E 6F 42 75 67 4E 75 6D 30 32 2C 20 56 65
+72 73 69 6F 6E 3D 31 2E 30 2E 30 2E 30 2C 20 43
+75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 
+79 54 6F 6B 65 6E 3D 6E
+75 6C 6C
+    "MonoBugNum02, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
+ExternalObject
+05, 01 00 00 00     ID=1
+2F,
+4D 6F 6E 6F 42 75 67 4E 75 6D 32 2E 4D 6F 6E 6F
+42 75 67 4E 75 6D 30 32  2B 44 75 6D 6D 79 53 65
+72 69 61 6C 69 7A 61 62  6C 65 43 6C 61 73 73
+    " MonoBugNum2.MonoBugNum02+DummySerializableClass"
+01 00 00 00     Num. Fields
+07, 46 6F 6F 43 6F 64 65     "FooCode"
+00 type-tag=Primitive
+08 type-spec=Int32
+02 00 00 00    In Assembly ID=2
+values[]=
+02 00 00 00    (Int32)=2
+End
+0B
+
+* Mono 2.0.50727.42.bin
+00 01 00 00 00 FF FF FF  FF 01 00 00 00 00 00 00 00 Assembly
+0C, 02 00 00 00     ID=2
+0C, 4D 6F 6E 6F 42 75 67 4E 75 6D 30 32     "MonoBugNum02"
+ExternalObject
+05, 01 00 00 00     ID=1
+2F, 4D 6F 6E 6F 42 75 67 4E 75 6D 32 2E 4D 6F 6E  6F
+42 75 67 4E 75 6D 30 32 2B 44 75 6D 6D 79 53  65
+72 69 61 6C 69 7A 61 62 6C 65 43 6C 61 73 73 
+    " MonoBugNum2.MonoBugNum02+DummySerializableClass"
+01 00 00 00     NumFields=1
+07, 46 6F 6F 43 6F 64 65     "FooCode"
+00 type-tag=Primitive
+08 type-spec=Int32
+02 00 00 00    In Assembly ID=2
+values[]=
+ExternalObject
+05, 03 00 00 00     ID=3
+20, 4D 6F 6E 6F 42 75 67 4E 75 6D 32 2E 4D 6F 6E 6F
+42 75 67 4E 75 6D 30 32 2B 46 6F 6F 45 6E 75 6D
+    " MonoBugNum2.MonoBugNum02+FooEnum"
+01 00 00 00     NumFields=1
+07, 76 61 6C 75 65 5F 5F "value__"
+00     type-tag=Primitive
+02     type-spec=Byte
+02 00 00 00 In Assembly ID=2
+values[]=
+02     (Byte)=2
+End
+0B
+
+So we can see why the exception is "Unexpected binary element: 0...".  
+The deserializer is told that the type of the value is Int32 and thus 
+goes and reads the value as the next four bytes.  In the corrupt case it 
+thus reads "05, 03 00 00" as the value, which is actually the start of an 
+block describing the enum object, and starts reading again at 
+"00   20, 4D 6F, ...", with 0 not being a valid value.
+
+
+Now I can't work out what the AddValue(String, Object, Type) overload is 
+really intended for, and is intended to do.  My best guess is that one 
+passes the Type value so that it can check that the value you pass is of 
+the correct type for that field.  This is _slightly_ backed-up by the 
+MSDN documentation: "The Type to associate with the current object. This 
+parameter must always be the type of the object itself or of one of its 
+base classes.", the summary text is: "Adds a value into the 
+SerializationInfo store, where value is associated with name and is 
+serialized as being of Type type."
+
+So I set to writing a set of unit-tests to show that the MSFT version 
+will prohibit the writing of a value that is not of the correct type.  
+However I couldn't get it to throw in any case, getting as far as the 
+following without exception on writing:
+
+    [Serializable]
+    class Foo { public Foo() { } }
+    [Serializable]
+    class Bar { public Bar() { } }
+...
+            Foo value = new Foo();
+            info.AddValue("Name", value, typeof(Bar));
+
+So it (apparently) never throws!  I haven't yet looked at the output from 
+that case to see what on earth it does write...
+
+
+One way to stop this class of corrupt output is to do that that check.  
+Something like the following I presume:
+
+    void AddValue(String name, Object value, Type type) {
+       // Per MSDN:
+       // type--"This parameter must always be the type of
+       // the object itself or of one of its base classes.".
+       if(!type.InstanceOf(value)) {
+          throw new ArgumentException(...);
+       }
+       ... ...
+    }
+
+
+I've attached some of my test code.  Compile the C# file as a command-
+line app.  Then if running on Windows, in a Mono Command Prompt, run 
+RunBothWays.cmd.  It will run each of the following on both CLRs: simple 
+round-trip test (fails on Mono), writes the output to disk named per the 
+CLR, and then reads the opposite file back in.  The last shows that both 
+CLRs fail on reading the Mono-produced output.  There are also some NUnit 
+unit-tests in there two; the ones *aiming* to show that AddValue/3 would 
+fail if passed an inconsistent value and type...


More information about the mono-bugs mailing list