From 6b64a6b331eb687d8609616f467e0cb48ebc9336 Mon Sep 17 00:00:00 2001 From: Arthur Chan Date: Fri, 8 Dec 2023 16:42:29 +0000 Subject: [PATCH] jackson-datatypes-collections: Fix OOM on Iterable creation of primitive byte array (#11339) This PR fixes 3 false positive issues in https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=64623, https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=64625 and https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=64634 which accidentally loops a very large byte array for too many times. In `GuavaSerailizerFuzzer` case 11. The logic uses a combination of `Iterables.cycle(T...)` and `Iterables.limit(Iterable, int)` to create an iterator for the fuzzing process. But there is a bug in the creation causing OOM. In Java, the generic type application is only possible for non-primitive type. For example, if the following code is run, the result is 2 and `elements[0] = 1` and `elements[1] = 2` inside `genericTest(T...)` because the generic type retrieves the Integer type successfully and `T` is treated as `Integer`. ```java public class Test { public static void main (String[] args) { Integer[] test = {1, 2}; genericTest(test); } public static T genericTest(T...elements) { System.out.println(elements.length); } } ``` But if the code changes to use a primitive type array instead like the following, the result is 2 and `elements[0][0] = 1` and `elements[0][1] = 2` inside `genericTest(T...)` because the generic type fails to convert the primitive type and thus it treats `T` as `int[]`. ```java public class Test { public static void main (String[] args) { int[] test = {1, 2}; genericTest(test); } public static T genericTest(T...elements) { System.out.println(elements.length); } } ``` Because of the above reason, the original code in `GuavaSerializerFuzzer` shown below provides a `byte[]` to the `Iterables.cycle(T...)` method. Thus the later `Iterables.limit(Iterable, int)` is looping through the whole byte[] for limit time, instead of looping the byte elements in the byte[]. It results in OOM in some issues when the `data.consumeRemainingAsBytes()` returns a very large byte array (e.g. 10k bytes). Instead of looping each byte in the byte array, it actually loops the whole byte array as a single object for 10k times which uses up the heap memory and causes OOM. ```java Iterables.limit(Iterables.cycle(data.consumeRemainingAsBytes()), remainingBytes)) ``` This PR fixes the problem by using the `Bytes.asList()` to retrieve the correct iterable objects for the fuzzing. Signed-off-by: Arthur Chan --- .../GuavaSerializerFuzzer.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/projects/jackson-datatypes-collections/GuavaSerializerFuzzer.java b/projects/jackson-datatypes-collections/GuavaSerializerFuzzer.java index e232eaf7b..020ce533f 100644 --- a/projects/jackson-datatypes-collections/GuavaSerializerFuzzer.java +++ b/projects/jackson-datatypes-collections/GuavaSerializerFuzzer.java @@ -25,7 +25,6 @@ import com.google.common.collect.FluentIterable; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableRangeSet; import com.google.common.collect.ImmutableTable; -import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultiset; import com.google.common.collect.Multimap; import com.google.common.collect.Multiset; @@ -35,6 +34,7 @@ import com.google.common.collect.TreeRangeSet; import com.google.common.hash.HashCode; import com.google.common.net.HostAndPort; import com.google.common.net.InternetDomainName; +import com.google.common.primitives.Bytes; import java.io.IOException; /** This fuzzer targets the serialization methods of Guava objects */ @@ -122,9 +122,7 @@ public class GuavaSerializerFuzzer { .build(); mapper.writeValueAsString(immutableRangeSet); case 11: - Integer remainingBytes = data.remainingBytes(); - mapper.writeValueAsString( - Iterables.limit(Iterables.cycle(data.consumeRemainingAsBytes()), remainingBytes)); + mapper.writeValueAsString(Bytes.asList(data.consumeRemainingAsBytes()).iterator()); case 12: ArrayListMultimap arrayListMultimap = ArrayListMultimap.create(); arrayListMultimap.put(