Skip to content

CompatibleFieldSerializer fails with IndexOutOfBoundsException on field removal. #286

Closed
@solf

Description

@solf

Many kryo versions are affected, I've tried with 3.0.0, 2.24.0, 2.23.1 and more.

Using some rather complex data structures this can happen during deserialization with CompatibleFieldSerializer if I remove (comment out) field in Java class (in particular case it was a simple String field):

Caused by: java.lang.IndexOutOfBoundsException: Index: 8139, Size: 8139
    at java.util.ArrayList.RangeCheck(ArrayList.java:547)
    at java.util.ArrayList.get(ArrayList.java:322)
    at com.esotericsoftware.kryo.util.MapReferenceResolver.getReadObject(MapReferenceResolver.java:43)
    at com.esotericsoftware.kryo.Kryo.readReferenceOrNull(Kryo.java:805)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:677)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    ... 23 more

(this is using 2.24.0 but line numbers may be off by one or two because of debug output I had to add to track it down)

Unfortunately data model is proprietary so I can share serialized data + java classes.

However I did a bunch of digging around and here's what I found -- it seems that CompatibleFieldSerializer skips de-serialization for missing fields like this: inputChunked.nextChunks();

This works fine EXCEPT when the actual reading code (which is employed if field is present) creates an additional object in MapReferenceResolver. I've added debug code that dumps what is added to ReferenceResolver (format is [id] -> [object]) and here's what happens:
when field is present (deserialisation works fine):

7126 -> []
00:00 DEBUG: [kryo] Read object reference [com.esotericsoftware.kryo.util.MapReferenceResolver] 7065: MyClass[null:null]
cached field: com.esotericsoftware.kryo.serializers.UnsafeCacheFields.UnsafeObjectField
7127 -> 0001-01-01
cached field: com.esotericsoftware.kryo.serializers.UnsafeCacheFields.UnsafeObjectField
7128 -> 1
cached field: com.esotericsoftware.kryo.serializers.UnsafeCacheFields.UnsafeObjectField
7129 -> {}
7130 -> 2015-01-27

when field is removed (deserialization crashes later on):

7126 -> []
00:00 DEBUG: [kryo] Read object reference [com.esotericsoftware.kryo.util.MapReferenceResolver] 7065: MyClass[null:null]
cached field: com.esotericsoftware.kryo.serializers.UnsafeCacheFields.UnsafeObjectField
7127 -> 0001-01-01
00:00 DEBUG: [kryo] Skip obsolete field.
cached field: com.esotericsoftware.kryo.serializers.UnsafeCacheFields.UnsafeObjectField
7128 -> {}
7129 -> 2015-01-27
7130 -> DayAmount@1a70b8

Because of the field skip, object ID 7128 (which should be '1' according to 'normal' deserialization process) is skipped and causes all subsequent IDs to shift -- which ultimately causes deserialization to fail.

These conditions don't happen every time -- not every skipped field is going to cause this failure. But it does happen in my case (not on the first skipped field instance, but eventually).

And finally I've added stack trace dump -- in case of 7128 this is the stack trace that leads to reference resolver state being modified:

java.lang.Exception
    at com.esotericsoftware.kryo.util.MapReferenceResolver.setReadObject(MapReferenceResolver.java:39)
    at com.esotericsoftware.kryo.Kryo.reference(Kryo.java:823)
    at com.esotericsoftware.kryo.Kryo.readObjectOrNull(Kryo.java:731)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:113)
    at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.read(CompatibleFieldSerializer.java:91)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:679)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.read(CompatibleFieldSerializer.java:91)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:761)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:116)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:1)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:679)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.read(CompatibleFieldSerializer.java:91)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:761)
    at com.esotericsoftware.kryo.serializers.MapSerializer.read(MapSerializer.java:143)
    at com.esotericsoftware.kryo.serializers.MapSerializer.read(MapSerializer.java:1)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:679)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.read(CompatibleFieldSerializer.java:91)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:761)
    at com.esotericsoftware.kryo.serializers.MapSerializer.read(MapSerializer.java:143)
    at com.esotericsoftware.kryo.serializers.MapSerializer.read(MapSerializer.java:1)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:679)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.read(CompatibleFieldSerializer.java:91)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:679)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.read(CompatibleFieldSerializer.java:91)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:657)
<proprietary code redacted>

This problem is affecting my production code (i.e. I can't remove obsolete fields) so if there's anything else I can do to help tracking this down (without divulging proprietary info) -- do let me know please, I'll do the best I can to help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions