Skip to content

Deserialization fails with "Buffer underflow" after add a new field to record class (Issue with backward compatibility) #1012

Open
@investr777

Description

@investr777

Kryo version 5.5.0
Java 17

When data was serialized and after that a new field was added, deserilization fails with exception: com.esotericsoftware.kryo.kryo5.io.KryoBufferUnderflowException: Buffer underflow
Stacktrace

Exception in thread "main" com.esotericsoftware.kryo.kryo5.io.KryoBufferUnderflowException: Buffer underflow.
Serialization trace:
info (akta.dcs.session.live.hash.Data)
	at com.esotericsoftware.kryo.kryo5.io.Input.require(Input.java:221)
	at com.esotericsoftware.kryo.kryo5.io.Input.readVarIntFlag(Input.java:482)
	at com.esotericsoftware.kryo.kryo5.io.Input.readString(Input.java:777)
	at com.esotericsoftware.kryo.kryo5.serializers.DefaultSerializers$StringSerializer.read(DefaultSerializers.java:174)
	at com.esotericsoftware.kryo.kryo5.serializers.DefaultSerializers$StringSerializer.read(DefaultSerializers.java:164)
	at com.esotericsoftware.kryo.kryo5.Kryo.readObjectOrNull(Kryo.java:826)
	at com.esotericsoftware.kryo.kryo5.serializers.RecordSerializer.read(RecordSerializer.java:136)
	at com.esotericsoftware.kryo.kryo5.Kryo.readObject(Kryo.java:777)

Code example:

public record Data(
    String id
){}

===============================================
var kryoLocal = ThreadLocal.withInitial(() -> {
    Kryo kryo = new Kryo();
    kryo.setRegistrationRequired(false);
    kryo.setDefaultSerializer(CompatibleFieldSerializer.class);
    return kryo;
});

 var data = new Data("1");

  var output = new Output(1024, -1);
  kryoLocal.get().writeObject(output, data);
  output.flush();
  var serializedData = output.toBytes(); // [-126, 49]
  output.close();

After serialized, new field was added:

public record Data(
    String id,
   String info
){}

=================

var input = new Input(new byte[] {-126, 49});
var deserializedData = kryoLocal.get().readObject(input, Data.class);
input.close();

And eventually an exception is thrown: com.esotericsoftware.kryo.kryo5.io.KryoBufferUnderflowException: Buffer underflow.

But if convert Java record to class, everything works properly:
Code example:

public class Data {
    private String id;

    public Data() {
    }

    public Data(
        String id
    ) {
        this.id = id;
    }

    public String id() {
        return id;
    }
}

=====================================
var kryoLocal = ThreadLocal.withInitial(() -> {
            Kryo kryo = new Kryo();
            kryo.setRegistrationRequired(false);
            kryo.setDefaultSerializer(CompatibleFieldSerializer.class);
            return kryo;
        });

        var data = new Data("1");

        var output = new Output(1024, -1);
        kryoLocal.get().writeObject(output, data);
        output.flush();
        var serializedData = output.toBytes(); // [1, 105, -28, 3, -126, 49]
        output.close();

After serialized, new field was added:

public class Data {
    private String id;
    private String info;

    public Data() {
    }

    public Data(
        String id,
        String info
    ) {
        this.id = id;
        this.info = info;
    }

    public String id() {
        return id;
    }

    public String info() {
        return info;
    }
}

==========================================

var input = new Input(new byte[] {1, 105, -28, 3, -126, 49});
var deserializedData = kryoLocal.get().readObject(input, Data.class);
input.close();

As we can see, that serialized record and class have different bytes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions