From 935e0547e0892feb6c4aa7e7512371327386545c Mon Sep 17 00:00:00 2001 From: Surya Srinivasan Date: Sun, 1 Mar 2026 02:57:25 +0530 Subject: [PATCH 1/2] feat(java): add configurable deserialization size guardrails (#3411) --- .../java/org/apache/fory/config/Config.java | 16 +++++++ .../org/apache/fory/config/ForyBuilder.java | 12 +++++ .../collection/CollectionLikeSerializer.java | 8 ++++ .../serializer/collection/MapSerializers.java | 48 +++++++++++++++++++ .../collection/PrimitiveListSerializers.java | 14 ++++++ 5 files changed, 98 insertions(+) diff --git a/java/fory-core/src/main/java/org/apache/fory/config/Config.java b/java/fory-core/src/main/java/org/apache/fory/config/Config.java index e07ada4ec1..822177c65e 100644 --- a/java/fory-core/src/main/java/org/apache/fory/config/Config.java +++ b/java/fory-core/src/main/java/org/apache/fory/config/Config.java @@ -65,6 +65,8 @@ public class Config implements Serializable { private final boolean serializeEnumByName; private final int bufferSizeLimitBytes; private final int maxDepth; + private final int maxBinarySize; + private final int maxCollectionSize; private final float mapRefLoadFactor; public Config(ForyBuilder builder) { @@ -106,6 +108,8 @@ public Config(ForyBuilder builder) { serializeEnumByName = builder.serializeEnumByName; bufferSizeLimitBytes = builder.bufferSizeLimitBytes; maxDepth = builder.maxDepth; + maxBinarySize = builder.maxBinarySize; + maxCollectionSize = builder.maxCollectionSize; mapRefLoadFactor = builder.mapRefLoadFactor; } @@ -326,6 +330,8 @@ public boolean equals(Object o) { && scalaOptimizationEnabled == config.scalaOptimizationEnabled && xlang == config.xlang && compatibleMode == config.compatibleMode + && maxBinarySize == config.maxBinarySize + && maxCollectionSize == config.maxCollectionSize && Objects.equals(defaultJDKStreamSerializerType, config.defaultJDKStreamSerializerType) && longEncoding == config.longEncoding; } @@ -353,6 +359,8 @@ public int hashCode() { compressIntArray, compressLongArray, bufferSizeLimitBytes, + maxBinarySize, + maxCollectionSize, requireClassRegistration, suppressClassRegistrationWarnings, registerGuavaTypes, @@ -381,6 +389,14 @@ public int maxDepth() { return maxDepth; } + public int maxBinarySize() { + return maxBinarySize; + } + + public int maxCollectionSize() { + return maxCollectionSize; + } + /** Returns loadFactor of MacRef's writtenObjects. */ public float mapRefLoadFactor() { return mapRefLoadFactor; diff --git a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java index 7e4bc0fde2..ff3d923883 100644 --- a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java @@ -88,6 +88,8 @@ public final class ForyBuilder { int bufferSizeLimitBytes = 128 * 1024; MetaCompressor metaCompressor = new DeflaterMetaCompressor(); int maxDepth = 50; + int maxBinarySize = -1; + int maxCollectionSize = -1; float mapRefLoadFactor = 0.51f; public ForyBuilder() {} @@ -394,6 +396,16 @@ public ForyBuilder withMaxDepth(int maxDepth) { return this; } + public ForyBuilder withMaxBinarySize(int maxBinarySize) { + this.maxBinarySize = maxBinarySize; + return this; + } + + public ForyBuilder withMaxCollectionSize(int maxCollectionSize) { + this.maxCollectionSize = maxCollectionSize; + return this; + } + /** Set loadFactor of MapRefResolver writtenObjects. Default value is 0.51 */ public ForyBuilder withMapRefLoadFactor(float loadFactor) { Preconditions.checkArgument( diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionLikeSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionLikeSerializer.java index cee78a1638..940118d70b 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionLikeSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionLikeSerializer.java @@ -437,6 +437,14 @@ public T read(MemoryBuffer buffer) { */ public Collection newCollection(MemoryBuffer buffer) { numElements = buffer.readVarUint32Small7(); + + int maxCollectionSize = fory.getConfig().maxCollectionSize(); + if (maxCollectionSize > 0 && numElements > maxCollectionSize) { + throw new IllegalArgumentException( + "Collection size " + numElements + + " exceeds configured maxCollectionSize " + maxCollectionSize + ); + } if (constructor == null) { constructor = ReflectionUtils.getCtrHandle(type, true); } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java index aa80d00c6d..0d650de420 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java @@ -61,6 +61,14 @@ public HashMapSerializer(Fory fory) { @Override public HashMap newMap(MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); + int maxCollectionSize = fory.getConfig().maxCollectionSize(); + if (maxCollectionSize > 0 && numElements > maxCollectionSize) { + throw new IllegalArgumentException( + "Map size " + numElements + + " exceeds configured maxCollectionSize " + maxCollectionSize + ); + } + setNumElements(numElements); HashMap hashMap = new HashMap(numElements); fory.getRefResolver().reference(hashMap); @@ -81,6 +89,14 @@ public LinkedHashMapSerializer(Fory fory) { @Override public LinkedHashMap newMap(MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); + int maxCollectionSize = fory.getConfig().maxCollectionSize(); + if (maxCollectionSize > 0 && numElements > maxCollectionSize) { + throw new IllegalArgumentException( + "Map size " + numElements + + " exceeds configured maxCollectionSize " + maxCollectionSize + ); + } + setNumElements(numElements); LinkedHashMap hashMap = new LinkedHashMap(numElements); fory.getRefResolver().reference(hashMap); @@ -101,6 +117,14 @@ public LazyMapSerializer(Fory fory) { @Override public LazyMap newMap(MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); + int maxCollectionSize = fory.getConfig().maxCollectionSize(); + if (maxCollectionSize > 0 && numElements > maxCollectionSize) { + throw new IllegalArgumentException( + "Map size " + numElements + + " exceeds configured maxCollectionSize " + maxCollectionSize + ); + } + setNumElements(numElements); LazyMap map = new LazyMap(numElements); fory.getRefResolver().reference(map); @@ -269,6 +293,14 @@ public ConcurrentHashMapSerializer(Fory fory, Class type) { @Override public ConcurrentHashMap newMap(MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); + int maxCollectionSize = fory.getConfig().maxCollectionSize(); + if (maxCollectionSize > 0 && numElements > maxCollectionSize) { + throw new IllegalArgumentException( + "Map size " + numElements + + " exceeds configured maxCollectionSize " + maxCollectionSize + ); + } + setNumElements(numElements); ConcurrentHashMap map = new ConcurrentHashMap(numElements); fory.getRefResolver().reference(map); @@ -300,6 +332,14 @@ public MapSnapshot onMapWrite(MemoryBuffer buffer, ConcurrentSkipListMap value) @Override public ConcurrentSkipListMap newMap(MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); + int maxCollectionSize = fory.getConfig().maxCollectionSize(); + if (maxCollectionSize > 0 && numElements > maxCollectionSize) { + throw new IllegalArgumentException( + "Map size " + numElements + + " exceeds configured maxCollectionSize " + maxCollectionSize + ); + } + setNumElements(numElements); Comparator comparator = (Comparator) fory.readRef(buffer); ConcurrentSkipListMap map = new ConcurrentSkipListMap(comparator); @@ -496,6 +536,14 @@ public Object onMapCopy(Map map) { public Map newMap(MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); + int maxCollectionSize = fory.getConfig().maxCollectionSize(); + if (maxCollectionSize > 0 && numElements > maxCollectionSize) { + throw new IllegalArgumentException( + "Map size " + numElements + + " exceeds configured maxCollectionSize " + maxCollectionSize + ); + } + setNumElements(numElements); HashMap map = new HashMap<>(numElements); fory.getRefResolver().reference(map); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/PrimitiveListSerializers.java b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/PrimitiveListSerializers.java index d86556bb1f..c1ef7a87df 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/PrimitiveListSerializers.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/PrimitiveListSerializers.java @@ -82,6 +82,13 @@ public void write(MemoryBuffer buffer, Int8List value) { @Override public Int8List read(MemoryBuffer buffer) { int size = buffer.readVarUint32Small7(); + int maxBinarySize = fory.getConfig().maxBinarySize(); + if (maxBinarySize > 0 && size > maxBinarySize) { + throw new IllegalArgumentException( + "Binary size " + size + + " exceeds configured maxBinarySize " + maxBinarySize + ); + } byte[] array = new byte[size]; buffer.readBytes(array); return new Int8List(array); @@ -281,6 +288,13 @@ public void write(MemoryBuffer buffer, Uint8List value) { @Override public Uint8List read(MemoryBuffer buffer) { int size = buffer.readVarUint32Small7(); + int maxBinarySize = fory.getConfig().maxBinarySize(); + if (maxBinarySize > 0 && size > maxBinarySize) { + throw new IllegalArgumentException( + "Binary size " + size + + " exceeds configured maxBinarySize " + maxBinarySize + ); + } byte[] array = new byte[size]; buffer.readBytes(array); return new Uint8List(array); From 4b99b48793fc3d0173b64c4e8fc7d2b0c702a6ce Mon Sep 17 00:00:00 2001 From: Surya Srinivasan Date: Sun, 1 Mar 2026 03:19:51 +0530 Subject: [PATCH 2/2] style(java): apply spotless formatting --- .../collection/CollectionLikeSerializer.java | 11 ++-- .../serializer/collection/MapSerializers.java | 54 ++++++++++--------- .../collection/PrimitiveListSerializers.java | 12 ++--- 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionLikeSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionLikeSerializer.java index 940118d70b..c3ceab6642 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionLikeSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionLikeSerializer.java @@ -437,13 +437,14 @@ public T read(MemoryBuffer buffer) { */ public Collection newCollection(MemoryBuffer buffer) { numElements = buffer.readVarUint32Small7(); - + int maxCollectionSize = fory.getConfig().maxCollectionSize(); if (maxCollectionSize > 0 && numElements > maxCollectionSize) { - throw new IllegalArgumentException( - "Collection size " + numElements + - " exceeds configured maxCollectionSize " + maxCollectionSize - ); + throw new IllegalArgumentException( + "Collection size " + + numElements + + " exceeds configured maxCollectionSize " + + maxCollectionSize); } if (constructor == null) { constructor = ReflectionUtils.getCtrHandle(type, true); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java index 0d650de420..168ce503a8 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java @@ -63,10 +63,11 @@ public HashMap newMap(MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); int maxCollectionSize = fory.getConfig().maxCollectionSize(); if (maxCollectionSize > 0 && numElements > maxCollectionSize) { - throw new IllegalArgumentException( - "Map size " + numElements + - " exceeds configured maxCollectionSize " + maxCollectionSize - ); + throw new IllegalArgumentException( + "Map size " + + numElements + + " exceeds configured maxCollectionSize " + + maxCollectionSize); } setNumElements(numElements); @@ -91,10 +92,11 @@ public LinkedHashMap newMap(MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); int maxCollectionSize = fory.getConfig().maxCollectionSize(); if (maxCollectionSize > 0 && numElements > maxCollectionSize) { - throw new IllegalArgumentException( - "Map size " + numElements + - " exceeds configured maxCollectionSize " + maxCollectionSize - ); + throw new IllegalArgumentException( + "Map size " + + numElements + + " exceeds configured maxCollectionSize " + + maxCollectionSize); } setNumElements(numElements); @@ -119,10 +121,11 @@ public LazyMap newMap(MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); int maxCollectionSize = fory.getConfig().maxCollectionSize(); if (maxCollectionSize > 0 && numElements > maxCollectionSize) { - throw new IllegalArgumentException( - "Map size " + numElements + - " exceeds configured maxCollectionSize " + maxCollectionSize - ); + throw new IllegalArgumentException( + "Map size " + + numElements + + " exceeds configured maxCollectionSize " + + maxCollectionSize); } setNumElements(numElements); @@ -295,10 +298,11 @@ public ConcurrentHashMap newMap(MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); int maxCollectionSize = fory.getConfig().maxCollectionSize(); if (maxCollectionSize > 0 && numElements > maxCollectionSize) { - throw new IllegalArgumentException( - "Map size " + numElements + - " exceeds configured maxCollectionSize " + maxCollectionSize - ); + throw new IllegalArgumentException( + "Map size " + + numElements + + " exceeds configured maxCollectionSize " + + maxCollectionSize); } setNumElements(numElements); @@ -334,10 +338,11 @@ public ConcurrentSkipListMap newMap(MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); int maxCollectionSize = fory.getConfig().maxCollectionSize(); if (maxCollectionSize > 0 && numElements > maxCollectionSize) { - throw new IllegalArgumentException( - "Map size " + numElements + - " exceeds configured maxCollectionSize " + maxCollectionSize - ); + throw new IllegalArgumentException( + "Map size " + + numElements + + " exceeds configured maxCollectionSize " + + maxCollectionSize); } setNumElements(numElements); @@ -538,10 +543,11 @@ public Map newMap(MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); int maxCollectionSize = fory.getConfig().maxCollectionSize(); if (maxCollectionSize > 0 && numElements > maxCollectionSize) { - throw new IllegalArgumentException( - "Map size " + numElements + - " exceeds configured maxCollectionSize " + maxCollectionSize - ); + throw new IllegalArgumentException( + "Map size " + + numElements + + " exceeds configured maxCollectionSize " + + maxCollectionSize); } setNumElements(numElements); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/PrimitiveListSerializers.java b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/PrimitiveListSerializers.java index c1ef7a87df..529d003e52 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/PrimitiveListSerializers.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/PrimitiveListSerializers.java @@ -84,10 +84,8 @@ public Int8List read(MemoryBuffer buffer) { int size = buffer.readVarUint32Small7(); int maxBinarySize = fory.getConfig().maxBinarySize(); if (maxBinarySize > 0 && size > maxBinarySize) { - throw new IllegalArgumentException( - "Binary size " + size + - " exceeds configured maxBinarySize " + maxBinarySize - ); + throw new IllegalArgumentException( + "Binary size " + size + " exceeds configured maxBinarySize " + maxBinarySize); } byte[] array = new byte[size]; buffer.readBytes(array); @@ -290,10 +288,8 @@ public Uint8List read(MemoryBuffer buffer) { int size = buffer.readVarUint32Small7(); int maxBinarySize = fory.getConfig().maxBinarySize(); if (maxBinarySize > 0 && size > maxBinarySize) { - throw new IllegalArgumentException( - "Binary size " + size + - " exceeds configured maxBinarySize " + maxBinarySize - ); + throw new IllegalArgumentException( + "Binary size " + size + " exceeds configured maxBinarySize " + maxBinarySize); } byte[] array = new byte[size]; buffer.readBytes(array);