Skip to content

Commit 6a89249

Browse files
committed
BREAKING feat: use mutation framework for single-byte[] fuzz tests
1 parent 945c0e4 commit 6a89249

4 files changed

Lines changed: 84 additions & 2 deletions

File tree

selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/ArgumentsMutatorFuzzTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ void fuzzPrimitiveArrays(
226226
if (l0 != null) assertThat(l0.length).isAtMost(3);
227227
if (by0 != null) assertThat(by0.length).isAtMost(3);
228228
if (s0 != null) assertThat(s0.length).isAtMost(3);
229+
if (b1 != null) assertThat(b1.length).isAtMost(1000);
230+
if (by1 != null) assertThat(by1.length).isAtMost(4096);
229231
}
230232

231233
enum MyEnum {

src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetRunner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public final class FuzzTargetRunner {
140140
useMutatorFramework =
141141
Opt.mutatorFramework.get()
142142
&& Opt.autofuzz.get().isEmpty()
143-
&& !(fuzzTarget.usesPrimitiveByteArray() || fuzzTarget.usesFuzzedDataProvider());
143+
&& !fuzzTarget.usesFuzzedDataProvider();
144144

145145
useFuzzedDataProvider = fuzzTarget.usesFuzzedDataProvider();
146146
if (!useFuzzedDataProvider && IS_ANDROID) {

src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/PrimitiveArrayMutatorFactory.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ public Optional<SerializingMutator<?>> tryCreate(
7373
public static final class PrimitiveArrayMutator<T> extends SerializingMutator<T> {
7474
private static final int DEFAULT_MIN_LENGTH = 0;
7575
private static final int DEFAULT_MAX_LENGTH = 1000;
76+
// This default is chosen to match libFuzzer's default max length for byte arrays.
77+
private static final int DEFAULT_BYTE_ARRAY_MAX_LENGTH = 4096;
7678
private static final Charset FUZZED_DATA_CHARSET = Charset.forName("CESU-8");
7779
private long minRange;
7880
private long maxRange;
@@ -216,7 +218,12 @@ private void extractRange(AnnotatedType type) {
216218
private void extractLength(AnnotatedType type) {
217219
Optional<WithLength> withLength = Optional.ofNullable(type.getAnnotation(WithLength.class));
218220
minLength = withLength.map(WithLength::min).orElse(DEFAULT_MIN_LENGTH);
219-
maxLength = withLength.map(WithLength::max).orElse(DEFAULT_MAX_LENGTH);
221+
// Different default max lengths for byte[] and other primitive arrays to match libFuzzer.
222+
int defaultMaxLength =
223+
type.getType().getTypeName().equals("byte[]")
224+
? DEFAULT_BYTE_ARRAY_MAX_LENGTH
225+
: DEFAULT_MAX_LENGTH;
226+
maxLength = withLength.map(WithLength::max).orElse(defaultMaxLength);
220227
}
221228

222229
private AnnotatedType convertWithLength(AnnotatedType type, AnnotatedType newType) {

src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/PrimitiveArrayMutatorTest.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,19 @@
2424
import static com.code_intelligence.jazzer.mutation.mutator.lang.PrimitiveArrayMutatorFactory.PrimitiveArrayMutator.getLongPrimitiveArray;
2525
import static com.code_intelligence.jazzer.mutation.mutator.lang.PrimitiveArrayMutatorFactory.PrimitiveArrayMutator.getShortPrimitiveArray;
2626
import static com.code_intelligence.jazzer.mutation.mutator.lang.PrimitiveArrayMutatorFactory.PrimitiveArrayMutator.makePrimitiveArrayToBytesConverter;
27+
import static com.code_intelligence.jazzer.mutation.support.TypeSupport.notNull;
28+
import static com.code_intelligence.jazzer.mutation.support.TypeSupport.withLength;
2729
import static com.google.common.truth.Truth.assertThat;
2830
import static org.junit.jupiter.params.provider.Arguments.arguments;
2931

32+
import com.code_intelligence.jazzer.mutation.api.SerializingMutator;
33+
import com.code_intelligence.jazzer.mutation.engine.ChainedMutatorFactory;
3034
import com.code_intelligence.jazzer.mutation.support.TypeHolder;
35+
import java.io.ByteArrayInputStream;
36+
import java.io.ByteArrayOutputStream;
37+
import java.io.DataInputStream;
38+
import java.io.DataOutputStream;
39+
import java.io.IOException;
3140
import java.lang.reflect.AnnotatedArrayType;
3241
import java.lang.reflect.AnnotatedType;
3342
import java.util.function.Function;
@@ -636,4 +645,68 @@ void testRoundTrip_bytes(byte[] numbers) {
636645
assertThat(libfuzzerBytesToBytes.apply(bytesToLibfuzzerBytes.apply(numbers)))
637646
.isEqualTo(numbers);
638647
}
648+
649+
/*
650+
Testing that the maxRange for the byte[] type is 4096 bytes, which is the default max range for byte[] in Jazzer.
651+
This is important to ensure that the mutator does not generate byte arrays that are too large, which could lead to out-of-memory errors during fuzzing.
652+
*/
653+
static Stream<Arguments> byteArrayLength() {
654+
AnnotatedType defaultLen = new TypeHolder<byte[]>() {}.annotatedType();
655+
AnnotatedType defaultLenNN = notNull(new TypeHolder<byte[]>() {}.annotatedType());
656+
AnnotatedType withLen_10_20 = withLength(new TypeHolder<byte[]>() {}.annotatedType(), 10, 20);
657+
AnnotatedType withLen_10_5000 =
658+
withLength(new TypeHolder<byte[]>() {}.annotatedType(), 10, 5000);
659+
AnnotatedType withLen_10_20_NN =
660+
withLength(notNull(new TypeHolder<byte[]>() {}.annotatedType()), 10, 20);
661+
AnnotatedType withLen_10_5000_NN =
662+
withLength(notNull(new TypeHolder<byte[]>() {}.annotatedType()), 10, 5000);
663+
// type, numBytes to serialize with write(), expectedNumBytes after deserialization with read()
664+
return Stream.of(
665+
arguments(defaultLen, 5000, 4096),
666+
arguments(defaultLen, 100, 100),
667+
arguments(defaultLen, 0, 0),
668+
arguments(defaultLenNN, 5000, 4096),
669+
arguments(defaultLenNN, 100, 100),
670+
arguments(defaultLenNN, 0, 0),
671+
arguments(withLen_10_20, 15, 15),
672+
arguments(withLen_10_20, 25, 20),
673+
arguments(withLen_10_5000, 15, 15),
674+
arguments(withLen_10_5000, 25, 25),
675+
arguments(withLen_10_5000, 5000, 5000),
676+
arguments(withLen_10_5000, 10000, 5000),
677+
arguments(withLen_10_20_NN, 15, 15),
678+
arguments(withLen_10_20_NN, 25, 20),
679+
arguments(withLen_10_5000_NN, 15, 15),
680+
arguments(withLen_10_5000_NN, 25, 25),
681+
arguments(withLen_10_5000_NN, 5000, 5000),
682+
arguments(withLen_10_5000_NN, 10000, 5000));
683+
}
684+
685+
@ParameterizedTest
686+
@MethodSource("byteArrayLength")
687+
public void testByteArrayMaxRange_writeReadWithLength(
688+
AnnotatedType type, int serializationLength, int expectedReadLength) throws IOException {
689+
ChainedMutatorFactory factory = ChainedMutatorFactory.of(LangMutators.newFactories());
690+
SerializingMutator<byte[]> mutator = (SerializingMutator<byte[]>) factory.createOrThrow(type);
691+
692+
byte[] serialized;
693+
694+
// serialize with write()
695+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
696+
mutator.write(new byte[serializationLength], new DataOutputStream(baos));
697+
serialized = baos.toByteArray();
698+
699+
// deserialize with read()
700+
byte[] data = mutator.read(new DataInputStream(new ByteArrayInputStream(serialized)));
701+
assertThat(data.length).isEqualTo(expectedReadLength);
702+
703+
// serialize with writeExclusive()
704+
baos = new ByteArrayOutputStream();
705+
mutator.writeExclusive(new byte[serializationLength], new DataOutputStream(baos));
706+
serialized = baos.toByteArray();
707+
708+
// deserialize with readExclusive()
709+
data = mutator.readExclusive(new DataInputStream(new ByteArrayInputStream(serialized)));
710+
assertThat(data.length).isEqualTo(expectedReadLength);
711+
}
639712
}

0 commit comments

Comments
 (0)