EsotericSoftware/kryo

Unbounded new int[length] from declared array length in Input.readInts → OutOfMemoryError on malformed input

Closed

#1,282 opened on Jun 16, 2026

 (3 comments) (0 reactions) (0 assignees)HTML (840 forks)batch import
bughelp wantedkryo 6

Repository metrics

Stars
 (6,539 stars)
PR merge metrics
 (Avg merge 107d 5h) (9 merged PRs in 30d)

Description

Describe the bug

When reading a primitive array, Kryo allocates the array from the declared length before validating it against the bytes actually remaining in the input. A few-byte input that declares a huge length causes an immediate OutOfMemoryError (new int[length] with no bound). Input.readInts and its siblings (readLongs/readFloats/readDoubles/...) and the array/collection serializers share this pattern.

Reporting this as a robustness/hardening issue. I am aware Kryo is not intended for deserializing untrusted data; filing in case bounding the initial allocation is considered worthwhile.

To Reproduce

Minimal runnable example (5-byte input):

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.*;

public class KryoPoc {
  public static void main(String[] a) {
    Kryo kryo = new Kryo();
    kryo.register(int[].class);          // typical application registration
    Output o = new Output(16);
    o.writeVarInt(2000000001, true);     // length+1 -> declared int[] length 2,000,000,000
    o.flush();
    byte[] evil = o.toBytes();           // 5 bytes; no element payload follows
    kryo.readObject(new Input(evil), int[].class);   // OutOfMemoryError here
  }
}

Result:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at com.esotericsoftware.kryo.io.Input.readInts(Input.java:968)

The input declares a 2,000,000,000-element array but carries no element data; Kryo allocates new int[2000000000] before checking that the input could possibly contain that many elements.

Environment:

  • OS: macOS 26.5.1 (25F80)
  • JDK Version: 21
  • Kryo Version: 5.6.2 (latest)

Suggested fix

Bound the initial allocation to the bytes remaining in the input (a primitive element is at least 1 byte, so the declared length cannot exceed the remaining byte count), or read and grow in chunks rather than pre-allocating the full declared length. Found via boundary-value testing as part of academic research.

Contributor guide