Skip to content

NullReferenceException in XmlObjectSerializerReadContext.ReplaceDeserializedObject #41465

Closed

Description

@eerhardt

It is possible for null objects to be passed to XmlObjectSerializerReadContext.ReplaceDeserializedObject:

public void ReplaceDeserializedObject(string id, object oldObj, object newObj)
{
if (object.ReferenceEquals(oldObj, newObj))
return;
if (id != Globals.NewObjectId)
{
// In certain cases (IObjectReference, SerializationSurrogate or DataContractSurrogate),
// an object can be replaced with a different object once it is deserialized. If the
// object happens to be referenced from within itself, that reference needs to be updated
// with the new instance. BinaryFormatter supports this by fixing up such references later.
// These XmlObjectSerializer implementations do not currently support fix-ups. Hence we
// throw in such cases to allow us add fix-up support in the future if we need to.
if (DeserializedObjects.IsObjectReferenced(id))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.FactoryObjectContainsSelfReference, DataContract.GetClrTypeFullName(oldObj.GetType()), DataContract.GetClrTypeFullName(newObj.GetType()), id)));
DeserializedObjects.Remove(id);
DeserializedObjects.Add(id, newObj);

The throw statement de-references the objects to call GetType() on them in order to log their types in the exception message. However, I can't find anything that ensures both of these parameters are non-null. If they are both null, the method exits early, but if one of them is null, it might be possible for this to throw NullReferenceException.

Of the 3 callers of this method, only 1 of them has the potential to pass in null:

private object InternalDeserializeWithSurrogate(XmlReaderDelegator xmlReader, Type declaredType, DataContract surrogateDataContract, string name, string ns)
{
DataContract dataContract = surrogateDataContract ??
GetDataContract(DataContractSurrogateCaller.GetDataContractType(_serializationSurrogateProvider, declaredType));
if (this.IsGetOnlyCollection && dataContract.UnderlyingType != declaredType)
{
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.Format(SR.SurrogatesWithGetOnlyCollectionsNotSupportedSerDeser, DataContract.GetClrTypeFullName(declaredType))));
}
ReadAttributes(xmlReader);
string objectId = GetObjectId();
object oldObj = InternalDeserialize(xmlReader, name, ns, declaredType, ref dataContract);
object obj = DataContractSurrogateCaller.GetDeserializedObject(_serializationSurrogateProvider, oldObj, dataContract.UnderlyingType, declaredType);
ReplaceDeserializedObject(objectId, oldObj, obj);

I can reproduce the NullReferenceException with the following code and .xml file:

using System;
using System.Runtime.Serialization;
using System.Xml;

namespace ConsoleApp101
{
    class Program
    {
        static void Main()
        {
            DataContractSerializer readerSerializer = new DataContractSerializer(typeof(CustomerContainer));
            DataContractSerializerExtensions.SetSerializationSurrogateProvider(readerSerializer, new MySerializationSurrogateProvider());

            using (var xmlreader = XmlReader.Create("temp.xml"))
            {
                readerSerializer.ReadObject(xmlreader);
            }
        }
    }

    class MySerializationSurrogateProvider : ISerializationSurrogateProvider
    {
        private int _customerCounter = 0;
        public object GetDeserializedObject(object obj, Type targetType)
        {
            if (targetType == typeof(Customer))
            {
                if (_customerCounter > 0)
                    return null;
                _customerCounter++;
            }
            return obj;
        }

        public object GetObjectToSerialize(object obj, Type targetType) => obj;
        public Type GetSurrogateType(Type type) => type;
    }

    [DataContract]
    class CustomerContainer
    {
        [DataMember]
        public Customer Customer1 { get; set; }
        [DataMember]
        public Customer Customer2 { get; set; }
    }

    [DataContract]
    class Customer
    {
        [DataMember]
        public string Name { get; set; }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<CustomerContainer xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApp101">
  <Customer1 z:Id="2">
    <Name z:Id="3">Hi</Name>
  </Customer1>
  <Customer2 z:Id="2" z:Ref="2" i:nil="true" />
</CustomerContainer>

Exception

Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Object.GetType()
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReplaceDeserializedObject(String id, Object oldObj, Object newObj)
   at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeWithSurrogate(XmlReaderDelegator xmlReader, Type declaredType, DataContract surrogateDataContract, String name, String ns)
   at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, String name, String ns)
   at ReadCustomerContainerFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )
   at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
   at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeWithSurrogate(XmlReaderDelegator xmlReader, Type declaredType, DataContract surrogateDataContract, String name, String ns)
   at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
   at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
   at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
   at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlReader reader)
   at ConsoleApp101.Program.Main() in C:\Users\eerhardt\source\repos\ConsoleApp101\Program.cs:line 16

Activity

  1. added this to the 6.0.0 milestone on Sep 23, 2020
  2. removed
    untriagedNew issue has not been triaged by the area owner
    on Sep 23, 2020
  3. ghost added
    in-prThere is an active PR which will close this issue when it is merged
    on Jul 8, 2021
  4. ghost removed
    in-prThere is an active PR which will close this issue when it is merged
    on Jul 15, 2021
  5. ghost locked as resolved and limited conversation to collaborators on Aug 14, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions