|
24 | 24 | import com.code_intelligence.jazzer.mutation.api.MutatorFactory; |
25 | 25 | import com.code_intelligence.jazzer.mutation.api.PseudoRandom; |
26 | 26 | 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; |
27 | 29 | import com.code_intelligence.jazzer.mutation.mutator.libfuzzer.LibFuzzerMutate; |
| 30 | +import com.code_intelligence.jazzer.mutation.support.DictionaryProviderSupport; |
28 | 31 | import com.code_intelligence.jazzer.mutation.support.RangeSupport; |
29 | 32 | import com.code_intelligence.jazzer.mutation.support.RangeSupport.LongRange; |
30 | 33 | import com.google.errorprone.annotations.ForOverride; |
|
33 | 36 | import java.io.IOException; |
34 | 37 | import java.lang.reflect.AnnotatedType; |
35 | 38 | import java.lang.reflect.ParameterizedType; |
| 39 | +import java.util.ArrayList; |
| 40 | +import java.util.List; |
36 | 41 | import java.util.Optional; |
| 42 | +import java.util.function.Function; |
37 | 43 | import java.util.function.Predicate; |
38 | 44 | import java.util.stream.LongStream; |
39 | 45 |
|
@@ -196,6 +202,8 @@ abstract static class AbstractIntegralMutator<T extends Number> extends Serializ |
196 | 202 | private final int largestMutableBitNegative; |
197 | 203 | private final int largestMutableBitPositive; |
198 | 204 | private final long[] specialValues; |
| 205 | + private final long[] dictionaryValues; |
| 206 | + private final Function<PseudoRandom, MutationFunction> mutationFunctionSampler; |
199 | 207 |
|
200 | 208 | AbstractIntegralMutator( |
201 | 209 | AnnotatedType type, long defaultMinValueForType, long defaultMaxValueForType) { |
@@ -231,6 +239,50 @@ abstract static class AbstractIntegralMutator<T extends Number> extends Serializ |
231 | 239 | largestMutableBitPositive = bitWidth(maxValue); |
232 | 240 | } |
233 | 241 | 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, |
234 | 286 | } |
235 | 287 |
|
236 | 288 | private static long[] collectSpecialValues(long minValue, long maxValue) { |
@@ -262,20 +314,25 @@ protected final long mutateImpl(long value, PseudoRandom prng) { |
262 | 314 | final long previousValue = value; |
263 | 315 | // Mutate in a loop to verify that we really mutated. |
264 | 316 | do { |
265 | | - switch (prng.indexIn(4)) { |
266 | | - case 0: |
| 317 | + switch (mutationFunctionSampler.apply(prng)) { |
| 318 | + case BIT_FLIP: |
267 | 319 | value = bitFlip(value, prng); |
268 | 320 | break; |
269 | | - case 1: |
| 321 | + case RANDOM_WALK: |
270 | 322 | value = randomWalk(value, prng); |
271 | 323 | break; |
272 | | - case 2: |
| 324 | + case RANDOM_VALUE: |
273 | 325 | value = prng.closedRange(minValue, maxValue); |
274 | 326 | break; |
275 | | - case 3: |
| 327 | + case LIBFUZZER: |
276 | 328 | // TODO: Replace this with a structure-aware dictionary/TORC search similar to fuzztest. |
277 | 329 | value = forceInRange(mutateWithLibFuzzer(value)); |
278 | 330 | break; |
| 331 | + case DICTIONARY_VALUE: |
| 332 | + value = dictionaryValues[prng.indexIn(dictionaryValues.length)]; |
| 333 | + break; |
| 334 | + default: |
| 335 | + throw new AssertionError("Invalid mutation function."); |
279 | 336 | } |
280 | 337 | } while (value == previousValue); |
281 | 338 | return value; |
|
0 commit comments