Skip to content

Add runtime type checks when setting properties during json deserialization #41146

@devsko

Description

@devsko

As mentiond in #40914 (comment), there should be runtime type checks before calling the property setter with the value returned from a custom converter. This should only be necessary when the converter type (like object / interface / base class) is not identical with the property type.

Here is what can happen without

        public static void Reinterpret()
        {
            var obj = new ObjectWrapper
            {
                Object = new WrittenObject
                {
                    Int = 123,
                    String = "Hello",
                }
            };

            obj = JsonSerializer.Deserialize<ObjectWrapper>(JsonSerializer.Serialize(obj));

            Debug.WriteLine(obj.GetType().GetProperty(nameof(ObjectWrapper.Object)).PropertyType.FullName);
            // System.Text.Json.Serialization.Tests.CustomConverterTests+WrittenObject

            Debug.WriteLine(obj.Object.GetType().FullName);
            // System.Text.Json.Serialization.Tests.CustomConverterTests+ReadObject

            Debug.WriteLine(Unsafe.As<ReadObject>(obj.Object).Double);
            // 3.141592653589793

            Debug.WriteLine(obj.Object.String.Length);
            // An unhandled exception of type 'System.AccessViolationException' occurred in System.Text.Json.Tests.dll
            // Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
        }

        public class ReinterpretConverter : JsonConverter<object>
        {
            public override bool CanConvert(Type typeToConvert)
                => true;

            public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
            {
                JsonSerializer.Deserialize<WrittenObject>(ref reader, options);
                return new ReadObject{ Double = Math.PI };
            }

            public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
            {
                JsonSerializer.Serialize<WrittenObject>(writer, (WrittenObject)value, options);
            }
        }

        private class ObjectWrapper
        {
            [JsonConverter(typeof(ReinterpretConverter))]
            public WrittenObject Object { get; set; }
        }

        private class WrittenObject
        {
            public string String { get; set; }
            public int Int { get; set; }
        }

        private class ReadObject
        {
            public double Double { get; set; }
        }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions