Skip to content

Commit 1c3fa67

Browse files
committed
feat: Integral mutators now use @DictionaryProvider
1 parent 9fe86da commit 1c3fa67

File tree

1 file changed

+62
-5
lines changed

1 file changed

+62
-5
lines changed

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

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
import com.code_intelligence.jazzer.mutation.api.MutatorFactory;
2525
import com.code_intelligence.jazzer.mutation.api.PseudoRandom;
2626
import com.code_intelligence.jazzer.mutation.api.SerializingMutator;
27+
import com.code_intelligence.jazzer.mutation.combinator.SamplingUtils;
28+
import com.code_intelligence.jazzer.mutation.combinator.SamplingUtils.WeightedValue;
2729
import com.code_intelligence.jazzer.mutation.mutator.libfuzzer.LibFuzzerMutate;
30+
import com.code_intelligence.jazzer.mutation.support.DictionaryProviderSupport;
2831
import com.code_intelligence.jazzer.mutation.support.RangeSupport;
2932
import com.code_intelligence.jazzer.mutation.support.RangeSupport.LongRange;
3033
import com.google.errorprone.annotations.ForOverride;
@@ -33,7 +36,10 @@
3336
import java.io.IOException;
3437
import java.lang.reflect.AnnotatedType;
3538
import java.lang.reflect.ParameterizedType;
39+
import java.util.ArrayList;
40+
import java.util.List;
3641
import java.util.Optional;
42+
import java.util.function.Function;
3743
import java.util.function.Predicate;
3844
import java.util.stream.LongStream;
3945

@@ -196,6 +202,8 @@ abstract static class AbstractIntegralMutator<T extends Number> extends Serializ
196202
private final int largestMutableBitNegative;
197203
private final int largestMutableBitPositive;
198204
private final long[] specialValues;
205+
private final long[] dictionaryValues;
206+
private final Function<PseudoRandom, MutationFunction> mutationFunctionSampler;
199207

200208
AbstractIntegralMutator(
201209
AnnotatedType type, long defaultMinValueForType, long defaultMaxValueForType) {
@@ -231,6 +239,50 @@ abstract static class AbstractIntegralMutator<T extends Number> extends Serializ
231239
largestMutableBitPositive = bitWidth(maxValue);
232240
}
233241
this.specialValues = collectSpecialValues(minValue, maxValue);
242+
243+
this.dictionaryValues =
244+
DictionaryProviderSupport.extractRawValues(type)
245+
.map(
246+
stream ->
247+
stream
248+
.filter(v -> v instanceof Number)
249+
.map(v -> ((Number) v).longValue())
250+
.filter(v -> v >= minValue)
251+
.filter(v -> v <= maxValue)
252+
.sorted()
253+
.mapToLong(Long::longValue)
254+
.toArray())
255+
.orElse(null);
256+
List<WeightedValue<MutationFunction>> f = new ArrayList<>();
257+
f.add(new WeightedValue<>(1.0, MutationFunction.BIT_FLIP));
258+
f.add(new WeightedValue<>(1.0, MutationFunction.RANDOM_WALK));
259+
f.add(new WeightedValue<>(1.0, MutationFunction.RANDOM_VALUE));
260+
f.add(new WeightedValue<>(1.0, MutationFunction.LIBFUZZER));
261+
if (dictionaryValues != null && dictionaryValues.length > 0) {
262+
// Since weights here are relative, we need to adjust the weight of user dictionary mutator
263+
// so that it is taken proportionate the inverse probability specified in the annotation.
264+
// Basically, we need to scale up the weight for pInv:
265+
// 1/p --- x?
266+
// 1- 1/p --- totalFuncWeights
267+
// x = (1/p * totalFuncWeights) / (1 - 1/p)
268+
// = totalFuncWeights / (p - 1)
269+
double totalFuncWeights = 0.0;
270+
for (WeightedValue<MutationFunction> wf : f) {
271+
totalFuncWeights += wf.weight;
272+
}
273+
int invProbability = DictionaryProviderSupport.extractFirstInvProbability(type);
274+
double perValueWeight = totalFuncWeights / (invProbability - 1);
275+
f.add(new WeightedValue<>(perValueWeight, MutationFunction.DICTIONARY_VALUE));
276+
}
277+
this.mutationFunctionSampler = SamplingUtils.weightedSampler(f);
278+
}
279+
280+
private enum MutationFunction {
281+
BIT_FLIP,
282+
DICTIONARY_VALUE,
283+
LIBFUZZER,
284+
RANDOM_VALUE,
285+
RANDOM_WALK,
234286
}
235287

236288
private static long[] collectSpecialValues(long minValue, long maxValue) {
@@ -262,20 +314,25 @@ protected final long mutateImpl(long value, PseudoRandom prng) {
262314
final long previousValue = value;
263315
// Mutate in a loop to verify that we really mutated.
264316
do {
265-
switch (prng.indexIn(4)) {
266-
case 0:
317+
switch (mutationFunctionSampler.apply(prng)) {
318+
case BIT_FLIP:
267319
value = bitFlip(value, prng);
268320
break;
269-
case 1:
321+
case RANDOM_WALK:
270322
value = randomWalk(value, prng);
271323
break;
272-
case 2:
324+
case RANDOM_VALUE:
273325
value = prng.closedRange(minValue, maxValue);
274326
break;
275-
case 3:
327+
case LIBFUZZER:
276328
// TODO: Replace this with a structure-aware dictionary/TORC search similar to fuzztest.
277329
value = forceInRange(mutateWithLibFuzzer(value));
278330
break;
331+
case DICTIONARY_VALUE:
332+
value = dictionaryValues[prng.indexIn(dictionaryValues.length)];
333+
break;
334+
default:
335+
throw new AssertionError("Invalid mutation function.");
279336
}
280337
} while (value == previousValue);
281338
return value;

0 commit comments

Comments
 (0)