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> 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> 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 <arthur.chan@adalogics.com>
This commit is contained in:
Arthur Chan 2023-12-08 16:42:29 +00:00 committed by GitHub
parent 3a8b77fdde
commit 6b64a6b331
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 2 additions and 4 deletions

View File

@ -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<String, Object> arrayListMultimap = ArrayListMultimap.create();
arrayListMultimap.put(