Skip to content

Commit 1ca7569

Browse files
committed
feat: @valuepool - propagate user values to types
This adds a new user-facing annotation @valuepool for wiring values directly to type mutators. ValuePoolMutatorFactory is prepended before (almost) every mutator.
1 parent ced2721 commit 1ca7569

File tree

14 files changed

+654
-11
lines changed

14 files changed

+654
-11
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ public class ArgumentsMutatorFuzzTest {
5050
static List<ArgumentsMutator> mutators =
5151
methods.stream()
5252
.map(
53-
m ->
54-
ArgumentsMutator.forMethod(Mutators.newFactory(), m)
55-
.orElseThrow(() -> new IllegalArgumentException("Invalid method: " + m)))
53+
method ->
54+
ArgumentsMutator.forMethod(Mutators.newFactory(), method)
55+
.orElseThrow(() -> new IllegalArgumentException("Invalid method: " + method)))
5656
.collect(Collectors.toList());
5757

5858
static {

src/main/java/com/code_intelligence/jazzer/junit/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ java_library(
3838
"//examples/junit/src/test/java/com/example:__pkg__",
3939
"//selffuzz/src/test/java/com/code_intelligence/selffuzz:__subpackages__",
4040
"//src/test/java/com/code_intelligence/jazzer/junit:__pkg__",
41+
"//src/test/java/com/code_intelligence/jazzer/mutation/support:__pkg__",
4142
],
4243
exports = [
4344
":lifecycle",

src/main/java/com/code_intelligence/jazzer/mutation/ArgumentsMutator.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import static java.util.Arrays.stream;
2424
import static java.util.stream.Collectors.joining;
2525

26+
import com.code_intelligence.jazzer.mutation.annotation.ValuePool;
2627
import com.code_intelligence.jazzer.mutation.api.ExtendedMutatorFactory;
2728
import com.code_intelligence.jazzer.mutation.api.PseudoRandom;
2829
import com.code_intelligence.jazzer.mutation.api.SerializingMutator;
@@ -31,6 +32,8 @@
3132
import com.code_intelligence.jazzer.mutation.engine.SeededPseudoRandom;
3233
import com.code_intelligence.jazzer.mutation.mutator.Mutators;
3334
import com.code_intelligence.jazzer.mutation.support.Preconditions;
35+
import com.code_intelligence.jazzer.mutation.support.TypeSupport;
36+
import com.code_intelligence.jazzer.mutation.support.ValuePoolRegistry;
3437
import com.code_intelligence.jazzer.utils.Log;
3538
import java.io.ByteArrayInputStream;
3639
import java.io.IOException;
@@ -75,15 +78,15 @@ private static String prettyPrintMethod(Method method) {
7578
}
7679

7780
public static ArgumentsMutator forMethodOrThrow(Method method) {
78-
return forMethod(Mutators.newFactory(), method)
81+
return forMethod(Mutators.newFactory(new ValuePoolRegistry(method)), method)
7982
.orElseThrow(
8083
() ->
8184
new IllegalArgumentException(
8285
"Failed to construct mutator for " + prettyPrintMethod(method)));
8386
}
8487

8588
public static Optional<ArgumentsMutator> forMethod(Method method) {
86-
return forMethod(Mutators.newFactory(), method);
89+
return forMethod(Mutators.newFactory(new ValuePoolRegistry(method)), method);
8790
}
8891

8992
public static Optional<ArgumentsMutator> forMethod(
@@ -97,11 +100,20 @@ public static Optional<ArgumentsMutator> forMethod(
97100
Log.error(validationError.getMessage());
98101
throw validationError;
99102
}
103+
104+
ValuePool[] valuePools = method.getAnnotationsByType(ValuePool.class);
105+
100106
return toArrayOrEmpty(
101107
stream(method.getAnnotatedParameterTypes())
102108
.map(
103109
type -> {
104-
Optional<SerializingMutator<?>> mutator = mutatorFactory.tryCreate(type);
110+
// Forward all @ValuePool annotations of the fuzz test method to each
111+
// arg.
112+
AnnotatedType t = type;
113+
for (ValuePool dict : valuePools) {
114+
t = TypeSupport.withExtraAnnotations(t, dict);
115+
}
116+
Optional<SerializingMutator<?>> mutator = mutatorFactory.tryCreate(t);
105117
if (!mutator.isPresent()) {
106118
Log.error(
107119
String.format(

src/main/java/com/code_intelligence/jazzer/mutation/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ java_library(
1212
"//src/main/java/com/code_intelligence/jazzer/mutation/engine",
1313
"//src/main/java/com/code_intelligence/jazzer/mutation/mutator",
1414
"//src/main/java/com/code_intelligence/jazzer/mutation/support",
15+
"//src/main/java/com/code_intelligence/jazzer/mutation/utils",
1516
"//src/main/java/com/code_intelligence/jazzer/utils:log",
1617
],
1718
)
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright 2024 Code Intelligence GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.code_intelligence.jazzer.mutation.annotation;
18+
19+
import static com.code_intelligence.jazzer.mutation.utils.PropertyConstraint.RECURSIVE;
20+
import static java.lang.annotation.ElementType.TYPE_USE;
21+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
22+
23+
import com.code_intelligence.jazzer.mutation.utils.IgnoreRecursiveConflicts;
24+
import com.code_intelligence.jazzer.mutation.utils.PropertyConstraint;
25+
import java.lang.annotation.ElementType;
26+
import java.lang.annotation.Retention;
27+
import java.lang.annotation.Target;
28+
29+
/**
30+
* Provides values to user-selected mutator types to start fuzzing from. Currently supported
31+
* mutators are:
32+
*
33+
* <ul>
34+
* <li>String mutator
35+
* <li>Integral mutators (byte, short, int, long)
36+
* </ul>
37+
*
38+
* <p>This annotation can be applied to fuzz test methods and any parameter type or subtype. By
39+
* default, this annotation is propagated to all nested subtypes unless specified otherwise via the
40+
* {@link #constraint()} attribute.
41+
*
42+
* <p>Example usage:
43+
*
44+
* <pre>{@code
45+
* public class MyFuzzTargets {
46+
*
47+
* static Stream<?> valluesVisibleByAllArgumentMutators() {
48+
* return Stream.of("example1", "example2", "example3", 1232187321, -182371);
49+
* }
50+
*
51+
* static Stream<?> valuesVisibleOnlyByAnotherInput() {
52+
* return Stream.of("code-intelligence.com", "secret.url.1082h3u21ibsdsazuvbsa.com");
53+
* }
54+
*
55+
* @ValuePool("valuesVisibleByAllArgumentMutators")
56+
* @FuzzTest
57+
* public void fuzzerTestOneInput(String input, @ValuePool("valuesVisibleOnlyByAnotherInput") String anotherInput) {
58+
* // Fuzzing logic here
59+
* }
60+
* }
61+
* }</pre>
62+
*
63+
* In this example, the mutator for the String parameter {@code input} of the fuzz test method
64+
* {@code fuzzerTestOneInput} will be using the values returned by {@code provide} method during
65+
* mutation, while the mutator for String {@code anotherInput} will use values from both methods:
66+
* from the method-level {@code ValuePool} annotation that uses {@code provide} and the
67+
* parameter-level {@code ValuePool} annotation that uses {@code provideSomethingElse}.
68+
*/
69+
@Target({ElementType.METHOD, TYPE_USE})
70+
@Retention(RUNTIME)
71+
@IgnoreRecursiveConflicts
72+
@PropertyConstraint
73+
public @interface ValuePool {
74+
/**
75+
* Specifies supplier methods that generate values for fuzzing the annotated method or type. The
76+
* specified supplier methods must be static and return a {@code Stream <?>} of values. The values
77+
* don't need to match the type of the annotated method or parameter exactly. The mutation
78+
* framework will extract only the values that are compatible with the target type.
79+
*/
80+
String[] value() default {""};
81+
82+
/**
83+
* This {@code ValuePool} will be used with probability {@code 1/p} by the mutator responsible for
84+
* fitting types. Not all mutators respect this probability.
85+
*/
86+
double p() default 0.1;
87+
88+
/**
89+
* Defines the scope of the annotation. Possible values are defined in {@link
90+
* com.code_intelligence.jazzer.mutation.utils.PropertyConstraint}. By default it's {@code
91+
* RECURSIVE}.
92+
*/
93+
String constraint() default RECURSIVE;
94+
}

src/main/java/com/code_intelligence/jazzer/mutation/mutator/Mutators.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,23 @@
2727
import com.code_intelligence.jazzer.mutation.mutator.libfuzzer.LibFuzzerMutators;
2828
import com.code_intelligence.jazzer.mutation.mutator.proto.ProtoMutators;
2929
import com.code_intelligence.jazzer.mutation.mutator.time.TimeMutators;
30+
import com.code_intelligence.jazzer.mutation.support.ValuePoolRegistry;
3031
import java.util.stream.Stream;
3132

3233
public final class Mutators {
3334
private Mutators() {}
3435

3536
public static ExtendedMutatorFactory newFactory() {
37+
return newFactory(null);
38+
}
39+
40+
public static ExtendedMutatorFactory newFactory(ValuePoolRegistry valuePoolRegistry) {
3641
return ChainedMutatorFactory.of(
3742
new IdentityCache(),
3843
NonNullableMutators.newFactories(),
39-
LangMutators.newFactories(),
44+
LangMutators.newFactories(valuePoolRegistry),
4045
CollectionMutators.newFactories(),
41-
ProtoMutators.newFactories(),
46+
ProtoMutators.newFactories(valuePoolRegistry),
4247
LibFuzzerMutators.newFactories(),
4348
TimeMutators.newFactories(),
4449
// Keep generic aggregate mutators last in case a concrete type is also an aggregate type.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,21 @@
1717
package com.code_intelligence.jazzer.mutation.mutator.lang;
1818

1919
import com.code_intelligence.jazzer.mutation.api.MutatorFactory;
20+
import com.code_intelligence.jazzer.mutation.support.ValuePoolRegistry;
2021
import java.util.stream.Stream;
2122

2223
public final class LangMutators {
2324
private LangMutators() {}
2425

2526
public static Stream<MutatorFactory> newFactories() {
27+
return newFactories(null);
28+
}
29+
30+
public static Stream<MutatorFactory> newFactories(ValuePoolRegistry valuePoolRegistry) {
2631
return Stream.of(
2732
// DON'T EVER SORT THESE! The order is important for the mutator engine to work correctly.
2833
new NullableMutatorFactory(),
34+
new ValuePoolMutatorFactory(valuePoolRegistry),
2935
new BooleanMutatorFactory(),
3036
new FloatingPointMutatorFactory(),
3137
new IntegralMutatorFactory(),

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
package com.code_intelligence.jazzer.mutation.mutator.lang;
1818

1919
import static com.code_intelligence.jazzer.mutation.combinator.MutatorCombinators.mutateThenMapToImmutable;
20-
import static com.code_intelligence.jazzer.mutation.support.TypeSupport.*;
20+
import static com.code_intelligence.jazzer.mutation.support.TypeSupport.findFirstParentIfClass;
21+
import static com.code_intelligence.jazzer.mutation.support.TypeSupport.notNull;
22+
import static com.code_intelligence.jazzer.mutation.support.TypeSupport.withLength;
2123

2224
import com.code_intelligence.jazzer.mutation.annotation.Ascii;
2325
import com.code_intelligence.jazzer.mutation.annotation.UrlSegment;

0 commit comments

Comments
 (0)