From 8c2f9dbd6a545acf8a733591b087de65d5a616d0 Mon Sep 17 00:00:00 2001 From: Peter Samarin Date: Mon, 15 Sep 2025 16:22:48 +0200 Subject: [PATCH] fix(mutation): respect maxLength in LibFuzzerMutatorFactory.readExclusive Since users are free to change fuzz test signature, readExclusive might return byte arrays longer than maxLength. Calling libfuzzer's Mutate with maxSize that is less than the length of the array to be mutated might result in a segfault at the time when the data type is used in Java. Some of libfuzzer's mutators (Mutate_AddWordFromTORC) are not designed to handle this case and will mutate past the array bounds. Co-authored-by: Simon Resch --- .../mutator/libfuzzer/LibFuzzerMutate.java | 1 + .../libfuzzer/LibFuzzerMutatorFactory.java | 2 +- .../mutation/mutator/lang/StringMutatorTest.java | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/libfuzzer/LibFuzzerMutate.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/libfuzzer/LibFuzzerMutate.java index d32a4a5f4..230ad82e9 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/libfuzzer/LibFuzzerMutate.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/libfuzzer/LibFuzzerMutate.java @@ -33,6 +33,7 @@ public final class LibFuzzerMutate { public static final String MOCK_SIZE_KEY = "libfuzzermutator.mock.newsize"; public static byte[] mutateDefault(byte[] data, int maxSizeIncrease) { + require(maxSizeIncrease >= 0); byte[] mutatedBytes; if (maxSizeIncrease == 0) { mutatedBytes = data; diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/libfuzzer/LibFuzzerMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/libfuzzer/LibFuzzerMutatorFactory.java index 79e0632c7..d2cc897e0 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/libfuzzer/LibFuzzerMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/libfuzzer/LibFuzzerMutatorFactory.java @@ -71,7 +71,7 @@ public byte[] read(DataInputStream in) throws IOException { @Override public byte[] readExclusive(InputStream in) throws IOException { - return readAllBytes(in); + return enforceLength(readAllBytes(in)); } @Override diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/StringMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/StringMutatorTest.java index 4da06e7fe..b4e4fba8c 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/StringMutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/StringMutatorTest.java @@ -34,6 +34,8 @@ import com.code_intelligence.jazzer.mutation.support.TestSupport.MockPseudoRandom; import com.code_intelligence.jazzer.mutation.support.TypeHolder; import com.google.protobuf.ByteString; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.SplittableRandom; @@ -211,6 +213,19 @@ void testMinLengthMutate() { assertThat(s).isEqualTo("gqrff\0\0\0\0\0"); } + @Test + void testReadInputsLongerThanMaxLength() throws IOException { + SerializingMutator mutator = + (SerializingMutator) + factory.createOrThrow( + new TypeHolder<@NotNull @WithUtf8Length(max = 5) String>() {}.annotatedType()); + assertThat(mutator.toString()).isEqualTo("String"); + + ByteArrayInputStream in = new java.io.ByteArrayInputStream("foobarbazf".getBytes()); + String s = mutator.readExclusive(in); + assertThat(s).isEqualTo("fooba"); + } + @Test void testMaxLengthMutate() { SerializingMutator mutator =