Skip to content

Commit

Permalink
Copy-on-write input buffers when fuzzing
Browse files Browse the repository at this point in the history
  • Loading branch information
jon-bell committed Jul 31, 2024
1 parent 59172b7 commit 81b0101
Showing 1 changed file with 52 additions and 17 deletions.
69 changes: 52 additions & 17 deletions fuzz/src/main/java/edu/berkeley/cs/jqf/fuzz/ei/ZestGuidance.java
Original file line number Diff line number Diff line change
Expand Up @@ -1220,24 +1220,27 @@ public class LinearInput extends Input<Integer> {
/** The number of bytes requested so far */
protected int requested = 0;

protected LinearInput parent;

protected boolean dirty;

public LinearInput() {
super();
this.values = new ByteArrayList();
}

public LinearInput(LinearInput other) {
super(other);
this.values = new ByteArrayList(other.values.toArray());
this.parent = other;
}


@Override
public int getOrGenerateFresh(Integer key, Random random) {
// Otherwise, make sure we are requesting just beyond the end-of-list
// assert (key == values.size());
if (key != requested) {
throw new IllegalStateException(String.format("Bytes from linear input out of order. " +
"Size = %d, Key = %d", values.size(), key));
"Size = %d, Key = %d", this.size(), key));
}

// Don't generate over the limit
Expand All @@ -1246,10 +1249,10 @@ public int getOrGenerateFresh(Integer key, Random random) {
}

// If it exists in the list, return it
if (key < values.size()) {
if (key < this.size()) {
requested++;
// infoLog("Returning old byte at key=%d, total requested=%d", key, requested);
return values.get(key);
return this.get(key);
}

// Handle end of stream
Expand All @@ -1258,16 +1261,54 @@ public int getOrGenerateFresh(Integer key, Random random) {
} else {
// Just generate a random input
int val = random.nextInt(256);
values.add((byte) val);
this.add((byte) val);
requested++;
// infoLog("Generating fresh byte at key=%d, total requested=%d", key, requested);
return val;
}
}

private void add(byte b) {
if (!dirty) {
dirty = true;
if (this.parent == null) {
values = new ByteArrayList();
} else {
values = new ByteArrayList(this.parent.values.toArray());
}
}
values.add(b);
}

private void set(int pos, byte b) {
if (!dirty) {
dirty = true;
if (this.parent == null) {
values = new ByteArrayList();
} else {
values = new ByteArrayList(this.parent.values.toArray());
}
}
values.set(pos, b);
}

private int get(int pos) {
if (this.dirty) {
return Byte.toUnsignedInt(values.get(pos));
} else {
return this.parent.get(pos);
}
}

@Override
public int size() {
return values.size();
if (this.dirty) {
return values.size();
} else if (this.parent == null) {
return 0;
} else {
return this.parent.size();
}
}

/**
Expand Down Expand Up @@ -1304,21 +1345,21 @@ public Input fuzz(Random random) {
for (int mutation = 1; mutation <= numMutations; mutation++) {

// Select a random offset and size
int offset = random.nextInt(newInput.values.size());
int offset = random.nextInt(newInput.size());
int mutationSize = sampleGeometric(random, MEAN_MUTATION_SIZE);

// desc += String.format(":%d@%d", mutationSize, idx);

// Mutate a contiguous set of bytes from offset
for (int i = offset; i < offset + mutationSize; i++) {
// Don't go past end of list
if (i >= newInput.values.size()) {
if (i >= newInput.size()) {
break;
}

// Otherwise, apply a random mutation
int mutatedValue = setToZero ? 0 : random.nextInt(256);
newInput.values.set(i, (byte) mutatedValue);
newInput.set(i, (byte) mutatedValue);
}
}

Expand All @@ -1327,13 +1368,7 @@ public Input fuzz(Random random) {

@Override
public void writeTo(BufferedOutputStream out) throws IOException {
ByteIterator iter = this.values.byteIterator();
while (iter.hasNext()) {
int b = Byte.toUnsignedInt(iter.next());
//For compatibility, we write the byte as an int
assert (b >= 0 && b < 256);
out.write(b);
}
out.write(this.values.toArray());
}
}

Expand Down

0 comments on commit 81b0101

Please sign in to comment.