EsotericSoftware / EsotericSoftware/kryo

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

Open
#1,012 3 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
HTML
Stars
6.5k
Forks
841
Avg merge
39m
Merged PRs (30d)
4

Description

**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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.