Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@

import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.code_intelligence.jazzer.junit.FuzzTest;
import com.code_intelligence.jazzer.mutation.annotation.WithSize;
import com.code_intelligence.jazzer.mutation.annotation.WithUtf8Length;
import com.code_intelligence.jazzer.protobuf.Proto2;
import com.code_intelligence.jazzer.protobuf.Proto3;
import com.code_intelligence.selffuzz.jazzer.mutation.ArgumentsMutator;
import com.code_intelligence.selffuzz.jazzer.mutation.annotation.NotNull;
import com.code_intelligence.selffuzz.jazzer.mutation.annotation.WithLength;
import com.code_intelligence.selffuzz.jazzer.mutation.annotation.WithSize;
import com.code_intelligence.selffuzz.jazzer.mutation.annotation.WithUtf8Length;
import com.code_intelligence.selffuzz.jazzer.mutation.mutator.Mutators;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
Expand All @@ -42,6 +42,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class ArgumentsMutatorFuzzTest {
Expand Down Expand Up @@ -102,21 +103,39 @@ void fuzzStrings(
@NotNull String s1,
@NotNull @WithUtf8Length(min = 10, max = 20) String s2) {}

@SelfFuzzTest // BUG: null pointer exception
void fuzzListOfMaps(Map<String, Integer> nullableMap) {}
@SelfFuzzTest
void fuzzListOfMaps(@WithSize(max = 4) Map<String, Integer> nullableMap) {
if (nullableMap != null) {
assertThat(nullableMap.size()).isAtMost(4);
}
}

@SelfFuzzTest
void fuzzListOfSets(@WithSize(max = 10) @NotNull Set<@NotNull Integer> setWithSize) {
if (setWithSize != null) {
assertThat(setWithSize.size()).isAtMost(10);
}
}

@SelfFuzzTest
void fuzzListOfLists(List<@NotNull List<String>> nullableMap, List<List<Integer>> nullableList) {}

@SelfFuzzTest
void fuzzPPrimitiveArrays(
int @WithLength(max = 10) [] a0, boolean[] a2, int @WithLength(max = 8193) [] a3) {}
void fuzzPrimitiveArrays(
int @WithLength(max = 10) [] a0, boolean[] a2, int @WithLength(max = 8193) [] a3) {
if (a0 != null) assertThat(a0.length).isAtMost(10);
if (a3 != null) assertThat(a3.length).isAtMost(8193);
}

@SelfFuzzTest
void fuzzBean(@NotNull ConstructorPropertiesAnnotatedBean bean, BeanWithParent beanWithParent) {}

@SelfFuzzTest
void fuzzListOfBeans(@WithSize(max = 4) List<BeanWithParent> beanWithParent) {}
void fuzzListOfBeans(@WithSize(max = 4) List<BeanWithParent> beanWithParent) {
if (beanWithParent != null) {
assertThat(beanWithParent.size()).isAtMost(4);
}
}

@SelfFuzzTest
void fuzzListOfListOfBeans(
Expand Down Expand Up @@ -189,7 +208,15 @@ void fuzzPrimitiveArrays(
Byte @WithLength(max = 3) [] by0,
byte[] by1,
Short @WithLength(max = 3) [] s0,
short[] s1) {}
short[] s1) {
if (i0 != null) assertThat(i0.length).isAtMost(3);
if (b0 != null) assertThat(b0.length).isAtMost(3);
if (d0 != null) assertThat(d0.length).isAtMost(3);
if (f0 != null) assertThat(f0.length).isAtMost(3);
if (l0 != null) assertThat(l0.length).isAtMost(3);
if (by0 != null) assertThat(by0.length).isAtMost(3);
if (s0 != null) assertThat(s0.length).isAtMost(3);
}

enum MyEnum {
A,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.lang.annotation.Target;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Generates a {@link List} or {@link Map} with the specified size.
Expand All @@ -35,7 +36,7 @@
*/
@Target(TYPE_USE)
@Retention(RUNTIME)
@AppliesTo({List.class, Map.class})
@AppliesTo({List.class, Map.class, Set.class})
@ValidateContainerDimensions
@PropertyConstraint
public @interface WithSize {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

final class ChunkCrossOvers {
private ChunkCrossOvers() {}
Expand Down Expand Up @@ -98,6 +100,25 @@ static <K, V> void insertChunk(
}
}

static <K> void insertChunk(
Set<K> set, Set<K> otherSet, int maxSize, PseudoRandom prng, boolean hasFixedSize) {
int originalSize = set.size();
int maxChunkSize = Math.min(maxSize - originalSize, otherSet.size());
int chunkSize = prng.sizeInClosedRange(1, maxChunkSize, hasFixedSize);
int fromChunkOffset = prng.closedRange(0, otherSet.size() - chunkSize);
Iterator<K> fromIterator = otherSet.iterator();
for (int i = 0; i < fromChunkOffset; i++) {
fromIterator.next();
}
// insertChunk only inserts new entries and does not overwrite existing
// ones. As skipping those entries would lead to fewer insertions than
// requested, loop over the rest of the set to fill the chunk if possible.
while (set.size() < originalSize + chunkSize && fromIterator.hasNext()) {
K key = fromIterator.next();
set.add(key);
}
}

static <K, V> void overwriteChunk(
Map<K, V> map, Map<K, V> otherMap, PseudoRandom prng, boolean hasFixedSize) {
onCorrespondingChunks(
Expand All @@ -117,6 +138,24 @@ static <K, V> void overwriteChunk(
hasFixedSize);
}

static <E> void overwriteChunk(
Set<E> set, Set<E> otherSet, PseudoRandom prng, boolean hasFixedSize) {
onCorrespondingChunks(
set,
otherSet,
prng,
(fromIterator, toIterator, chunkSize) -> {
Set<E> elementsToAdd = new LinkedHashSet<>(chunkSize);
for (int i = 0; i < chunkSize; i++) {
toIterator.next();
toIterator.remove();
elementsToAdd.add(fromIterator.next());
}
set.addAll(elementsToAdd);
},
hasFixedSize);
}

static <K, V> void crossOverChunk(
Map<K, V> map,
Map<K, V> otherMap,
Expand All @@ -130,6 +169,23 @@ static <K, V> void crossOverChunk(
}
}

static <E> void crossOverChunk(
Set<E> set, Set<E> otherSet, SerializingMutator<E> mutator, PseudoRandom prng) {
onCorrespondingChunks(
set,
otherSet,
prng,
(fromIterator, toIterator, chunkSize) -> {
Set<E> elementsToAdd = new LinkedHashSet<>(chunkSize);
for (int i = 0; i < chunkSize; i++) {
elementsToAdd.add(mutator.crossOver(toIterator.next(), fromIterator.next(), prng));
toIterator.remove();
}
set.addAll(elementsToAdd);
},
mutator.hasFixedSize());
}

private static <K, V> void crossOverChunkKeys(
Map<K, V> map, Map<K, V> otherMap, SerializingMutator<K> keyMutator, PseudoRandom prng) {
onCorrespondingChunks(
Expand Down Expand Up @@ -198,6 +254,11 @@ private interface ChunkMapOperation<K, V> {
void apply(Iterator<Entry<K, V>> fromIterator, Iterator<Entry<K, V>> toIterator, int chunkSize);
}

@FunctionalInterface
private interface ChunkSetOperation<E> {
void apply(Iterator<E> fromIterator, Iterator<E> toIterator, int chunkSize);
}

static <K, V> void onCorrespondingChunks(
Map<K, V> map,
Map<K, V> otherMap,
Expand All @@ -219,6 +280,32 @@ static <K, V> void onCorrespondingChunks(
operation.apply(fromIterator, toIterator, chunkSize);
}

static <K> void onCorrespondingChunks(
Set<K> set,
Set<K> otherSet,
PseudoRandom prng,
ChunkSetOperation<K> operation,
boolean hasFixedSize) {

if (set.isEmpty() || otherSet.isEmpty()) {
return;
}

int maxChunkSize = Math.min(set.size(), otherSet.size());
int chunkSize = prng.sizeInClosedRange(1, maxChunkSize, hasFixedSize);
int fromChunkOffset = prng.closedRange(0, otherSet.size() - chunkSize);
int toChunkOffset = prng.closedRange(0, set.size() - chunkSize);
Iterator<K> fromIterator = otherSet.iterator();
for (int i = 0; i < fromChunkOffset; i++) {
fromIterator.next();
}
Iterator<K> toIterator = set.iterator();
for (int i = 0; i < toChunkOffset; i++) {
toIterator.next();
}
operation.apply(fromIterator, toIterator, chunkSize);
}

public enum CrossOverAction {
INSERT_CHUNK,
OVERWRITE_CHUNK,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@

package com.code_intelligence.jazzer.mutation.mutator.collection;

import static com.code_intelligence.jazzer.mutation.support.Preconditions.require;

import com.code_intelligence.jazzer.mutation.api.PseudoRandom;
import com.code_intelligence.jazzer.mutation.api.SerializingMutator;
import com.code_intelligence.jazzer.mutation.api.ValueMutator;
import com.code_intelligence.jazzer.mutation.support.Preconditions;
import java.util.AbstractList;
import java.util.ArrayDeque;
import java.util.ArrayList;
Expand Down Expand Up @@ -162,6 +163,52 @@ static <K, V, KW, VW> boolean mutateRandomKeysChunk(
return grownBy > 0;
}

static <E> boolean mutateRandomChunk(
Set<E> set, SerializingMutator<E> elementMutator, PseudoRandom prng) {
int originalSize = set.size();
require(originalSize > 0, "mutateRandomSetChunk requires set size > 0");
int chunkSize = prng.sizeInClosedRange(1, originalSize, elementMutator.hasFixedSize());
int chunkOffset = prng.closedRange(0, originalSize - chunkSize);

Iterator<E> iterator = set.iterator();

// Skip to chunk offset
for (int i = 0; i < chunkOffset; i++) {
iterator.next();
}

// Collect elements to mutate and remove
List<E> originalElements = new ArrayList<>(chunkSize);
List<E> elementsToMutate = new ArrayList<>(chunkSize);
for (int i = 0; i < chunkSize; i++) {
E element = iterator.next();
originalElements.add(element);
elementsToMutate.add(elementMutator.detach(element));
}

// Try mutating each chunk element into a yet-not-present element in the set.
// Abort after MAX_FAILED_INSERTION_ATTEMPTS failed mutations in total.
int successCount = 0;
int failedAttemptsCount = 0;
for (E element : elementsToMutate) {
while (failedAttemptsCount < MAX_FAILED_INSERTION_ATTEMPTS) {
// Each element keeps getting mutated until we get a completely novel element.
element = elementMutator.mutate(element, prng);
if (set.add(element)) {
successCount++;
break;
} else {
failedAttemptsCount++;
}
}
}

for (int i = 0; i < successCount; i++) {
set.remove(originalElements.get(i));
}
return successCount > 0;
}

public static <K, V> void mutateRandomValuesChunk(
Map<K, V> map, ValueMutator<V> valueMutator, PseudoRandom prng) {
Collection<Map.Entry<K, V>> collection = map.entrySet();
Expand All @@ -182,7 +229,7 @@ public static <K, V> void mutateRandomValuesChunk(
static <T> boolean growBy(
Set<T> set, Consumer<T> addIfNew, int delta, Supplier<T> candidateSupplier) {
int oldSize = set.size();
Preconditions.require(delta >= 0);
require(delta >= 0);

final int targetSize = oldSize + delta;
int remainingAttempts = MAX_FAILED_INSERTION_ATTEMPTS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ public final class CollectionMutators {
private CollectionMutators() {}

public static Stream<MutatorFactory> newFactories() {
return Stream.of(new ListMutatorFactory(), new MapMutatorFactory(), new ArrayMutatorFactory());
return Stream.of(
new ListMutatorFactory(),
new MapMutatorFactory(),
new SetMutatorFactory(),
new ArrayMutatorFactory());
}
}
Loading