[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