Skip to content

Commit 9832e6c

Browse files
committed
chore: restructure the docs
1 parent f7734bc commit 9832e6c

File tree

6 files changed

+464
-309
lines changed

6 files changed

+464
-309
lines changed

README.md

Lines changed: 287 additions & 46 deletions
Large diffs are not rendered by default.

docs/advanced.md

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,4 @@
1-
## Advanced options
2-
3-
* [Using Jazzer Standalone](#using-jazzer-standalone)
4-
* [Passing JVM Arguments](#passing-jvm-arguments)
5-
* [Coverage Instrumentation](#coverage-instrumentation)
6-
* [Trace Instrumentation](#trace-instrumentation)
7-
* [Value Profile](#value-profile)
8-
* [Custom Hooks](#custom-hooks)
9-
* [Keep Going](#keep-going)
10-
* [Export Coverage Information](#export-coverage-information)
11-
* [Native Libraries](#native-libraries)
12-
13-
> [!NOTE]\
14-
> These settings apply to the old fuzzing approach using a `fuzzerTestOneInput` method and the native Jazzer binary. They don't work in the new JUnit integration.
1+
# Advanced options
152

163
## Using Jazzer Standalone
174
There are two ways to use Jazzer standalone: by using the `jazzer` binary or by calling the Jazzer main class directly.
@@ -39,7 +26,44 @@ Please refer to [libFuzzer](https://llvm.org/docs/LibFuzzer.html) for documentat
3926
Various command line options are available to control the instrumentation and fuzzer execution.
4027
A full list of command-line flags can be printed with the `--help` flag.
4128

42-
### Passing JVM Arguments
29+
30+
## Reproducing a finding
31+
32+
When Jazzer manages to find an input that causes an uncaught exception or a failed assertion, it prints a Java stack trace and creates two files that aid in reproducing the crash without Jazzer:
33+
34+
* `crash-<sha1_of_input>` contains the raw bytes passed to the fuzz target (just as with libFuzzer C/C++ fuzz targets).
35+
The crash can be reproduced with Jazzer by passing the path to the crash file as the only positional argument.
36+
* `Crash-<sha1_of_input>.java` contains a class with a `main` function that invokes the fuzz target with the crashing input.
37+
This is especially useful if using `FuzzedDataProvider` as the raw bytes of the input do not directly correspond to the values consumed by the fuzz target.
38+
The `.java` file can be compiled with just the fuzz target and its dependencies in the classpath (plus `jazzer_standalone.jar` or `com.code-intelligence:jazzer-api:<version>` if using `FuzzedDataProvider`).
39+
40+
## Minimizing a crashing input
41+
42+
With the following argument you can minimize a crashing input to find the smallest input that reproduces the same "bug":
43+
44+
```bash
45+
-minimize_crash=1 <path/to/crashing_input>
46+
```
47+
48+
## Parallel execution
49+
50+
libFuzzer offers the `-fork=N` and `-jobs=N` flags for parallel fuzzing, both of which are also supported by Jazzer.
51+
52+
53+
## Recommended JVM Options
54+
55+
The following JVM settings are recommended for running Jazzer:
56+
57+
* `-XX:-OmitStackTraceInFastThrow`: Ensures that stack traces are emitted even on hot code paths.
58+
This may hurt performance if your Fuzz Test frequently throws and catches exceptions, but also helps find flaky bugs.
59+
* `-XX:+UseParallelGC`: Optimizes garbage collection for high throughput rather than low latency.
60+
* `-XX:+CriticalJNINatives`: Is supported with JDK 17 and earlier and improves the runtime performance of Jazzer's
61+
instrumentation.
62+
* `-XX:+EnableDynamicAgentLoading`: Silences a warning with JDK 21 and later triggered by the Java agent that Jazzer
63+
attaches to instrument the fuzzed code.
64+
65+
66+
## Passing JVM Arguments
4367

4468
When Jazzer is started using the `jazzer` binary, it starts a JVM in which it executes the fuzz target.
4569
Arguments for this JVM can be provided via the `JAVA_OPTS` environment variable.
@@ -73,7 +97,7 @@ Both flags take a list of glob patterns for the java class name separated by col
7397
By default, JVM-internal classes and Java as well as Kotlin standard library classes are not instrumented,
7498
so these do not need to be excluded manually.
7599

76-
### Trace Instrumentation
100+
## Trace Instrumentation
77101

78102
The agent adds additional hooks for tracing compares, integer divisions, switch statements and array indices.
79103
These hooks correspond to [clang's data flow hooks](https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flow).
@@ -88,35 +112,22 @@ The particular instrumentation types to apply can be specified using the `--trac
88112

89113
Multiple instrumentation types can be combined with a colon (Linux, macOS) or a semicolon (Windows).
90114

91-
### Value Profile
115+
## Value Profile
92116

93117
The run-time flag `-use_value_profile=1` enables [libFuzzer's value profiling mode](https://llvm.org/docs/LibFuzzer.html#value-profile).
94118
When running with this flag, the feedback about compares and constants received from Jazzer's trace instrumentation is associated with the particular bytecode location and used to provide additional coverage instrumentation.
95119
See [ExampleValueProfileFuzzer.java](../examples/src/main/java/com/example/ExampleValueProfileFuzzer.java) for a fuzz target that would be very hard to fuzz without value profile.
96120

97-
### Custom hooks
98-
99-
In order to obtain information about data passed into functions such as `String.equals` or `String.startsWith`, Jazzer hooks invocations to these methods.
100-
This functionality is also available to fuzz targets, where it can be used to implement custom sanitizers or stub out methods that block the fuzzer from progressing (e.g. checksum verifications or random number generation).
101-
See [ExampleFuzzerHooks.java](../examples/src/main/java/com/example/ExampleFuzzerHooks.java) for an example of such a hook.
102-
An example for a sanitizer can be found in [ExamplePathTraversalFuzzerHooks.java](../examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java).
103-
104-
Method hooks can be declared using the `@MethodHook` annotation defined in the `com.code_intelligence.jazzer.api` package, which is contained in `jazzer_standalone.jar` (binary release) or in the Maven artifact [`com.code-intelligence:jazzer-api`](https://search.maven.org/search?q=g:com.code-intelligence%20a:jazzer-api).
105-
See the [javadocs of the `@MethodHook` API](https://codeintelligencetesting.github.io/jazzer-docs/jazzer-api/com/code_intelligence/jazzer/api/MethodHook.html) for more details.
106-
107-
To use the compiled method hooks, they have to be available on the classpath provided by `--cp` and can then be loaded by providing the flag `--custom_hooks`, which takes a colon-separated list of names of classes to load hooks from.
108-
Hooks have to be loaded from separate JAR files so that Jazzer can [add it to the bootstrap class loader search](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html#appendToBootstrapClassLoaderSearch-java.util.jar.JarFile-).
109-
The list of custom hooks can alternatively be specified via the `Jazzer-Hook-Classes` attribute in the fuzz target JAR's manifest.
110121

111-
### Keep Going
122+
## Keep Going
112123

113124
With the flag `--keep_going=N` Jazzer continues fuzzing until `N` unique stack traces have been encountered.
114125
Specifically `--keep-going=0` will keep the fuzzer running until another stop condition (e.g. maximum runtime) is met.
115126

116127
Particular stack traces can also be ignored based on their `DEDUP_TOKEN` by passing a comma-separated list of tokens via
117128
`--ignore=<token_1>,<token2>`.
118129

119-
### Export Coverage Information
130+
## Export Coverage Information
120131

121132
> [!WARNING]\
122133
> This feature is deprecated. The standalone JaCoCo agent should be used to generate coverage reports.
@@ -143,7 +154,7 @@ java -jar path/to/jacococli.jar report coverage.exec \
143154
--name FuzzCoverageReport
144155
```
145156

146-
### Native Libraries
157+
## Native Libraries
147158

148159
Jazzer supports fuzzing of native libraries loaded by the JVM, for example via `System.load()`.
149160
For the fuzzer to get coverage feedback, these libraries have to be compiled with `-fsanitize=fuzzer-no-link`.

docs/common.md

Lines changed: 0 additions & 92 deletions
This file was deleted.

docs/dev-implementation-details.md

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
## Implementation
2+
3+
Jazzer's JUnit integration starts from
4+
the [`FuzzTest`](../src/main/java/com/code_intelligence/jazzer/junit/FuzzTest.java) annotation. As mentioned in the
5+
annotation's javadoc, our integration runs in one of two modes: fuzzing and regression. Fuzzing mode will generate new
6+
inputs to feed into the tests to find new issues and regression mode will run the tests against previous findings, no
7+
fuzzing is done. The main entrypoints for the actual integration code are found in two of the annotations
8+
on `FuzzTest`: `@ArgumentsSource(FuzzTestArgumentsProvider.class)` and `@ExtendsWith(FuzzTestExtensions.class)`.
9+
10+
Because these same files and functions are involved in two mostly separate sets of functionality, this will look at the
11+
flow of the different methods involved in integrating with JUnit in fuzzing mode (when `JAZZER_FUZZ` is set to a truthy
12+
value (`true`, `1`, `yes`)) and in regression mode (when `JAZZER_FUZZ` is not set) separately.
13+
14+
### Fuzzing Flow
15+
16+
JUnit will call the following methods for each test marked with `FuzzTest`.
17+
18+
> [!NOTE]\
19+
> In fuzzing mode, only a single fuzz test is executed per test run
20+
> (see [#599](https://github.com/CodeIntelligenceTesting/jazzer/issues/599) for details).
21+
22+
#### `evaluateExecutionCondition`
23+
24+
The first call to this test will determine if the test should be run at all. In fuzzing mode, we only allow one test to
25+
be run due to global state in libfuzzer that would mean multiple tests would interfere with each other. Jazzer will
26+
accept the first fuzz test that is checked as the test to be run. It will cache which test it has seen first and
27+
return that test as enabled.
28+
29+
If this returns that a test is disabled, JUnit will not run the rest of these methods for this test and instead skip
30+
to the next one.
31+
32+
#### `provideArguments`
33+
34+
This will configure the fuzzing agent to set up code instrumentation, instantiate a `FuzzTestExecutor` and put it into
35+
JUnit's `extensionContext`, then create a stream of a single empty argument set. As the comment mentions, this is so
36+
that JUnit will actually execute the test but the argument will not be used.
37+
38+
#### `evaluateExecutionCondition`
39+
40+
This will be called for each argument set for the current test. In fuzzing mode, there will only be the single
41+
empty argument set which will be enabled.
42+
43+
#### `interceptTestTemplateMethod`
44+
45+
This will call `invocation.skip()` which prevents invoking the test function with the default set of
46+
arguments `provideArguments` created. It will instead extract the `FuzzTestExecutor` instance from
47+
the `extensionContext` and then calls `FuzzTestExecutor#execute` which creates a `FuzzTargetRunner` to run the actual
48+
fuzzing.
49+
50+
Crashes are saved in `resources/<package>/<test file name>Inputs/<test method name>` and results that are interesting to
51+
libfuzzer are saved in `.cifuzz-corpus`.
52+
53+
### Regression Flow
54+
55+
Similar to fuzzing mode, JUnit will call these methods for each test marked with `FuzzTest`.
56+
57+
#### `evaluateExecutionCondition`
58+
59+
This checks if the given test should be run at all. In regression mode, all tests are run so this will always return
60+
enabled.
61+
62+
#### `provideArguments`
63+
64+
This will configure the fuzzing agent as in fuzzing mode, then gather test cases to run from the following sources:
65+
66+
1. A default argument set of just an empty input
67+
2. A set of arguments from the associated resources directory
68+
3. If a `.cifuzz-corpus` directory exists, relevant entries from that are added as well
69+
70+
Prior to returning, the stream of test cases is put through `adaptInputsForFuzzTest` to turn the raw bytes from the
71+
files into the actual types to be given to the tested function.
72+
73+
##### Resources Tests
74+
75+
The tests from the resources directory are gathered by `walkInputs`. This will look for inputs in two places:
76+
77+
- `resources/<package>/<test class name>Inputs` - files found directly within this directory will be used as inputs for
78+
any tests within this class. This allows for easy sharing of corpus entries. Jazzer does not automatically put entries
79+
here, instead a human will need to decide a finding should be shared and manually move it.
80+
- `resources/<package>/<test class name>Inputs/<test method name>` - files found in this directory and any directory
81+
under it are used as inputs for only the test of the same name.
82+
83+
JUnit will use the file's name as the name of the test case for its reporting. It also accepts .jar files where it will
84+
search with the given directory in the jar.
85+
86+
> [!IMPORTANT]\
87+
> When adding these inputs files to a Git repository, consider creating a `.gitattributes` file containing
88+
> `src/test/resources/** binary` or similar to explicitly mark the files as binary files. Otherwise Git might
89+
> erroneously consider them text files, misinterpret bytes as line terminators and convert them on checkout.
90+
> This would change the content of the inputs files and can lead to unexpected behavior.
91+
92+
##### Corpus
93+
94+
The corpus kept in `.cifuzz-corpus/<test class name>/<test method name>` holds any inputs that libfuzzer found worth
95+
saving and not necessarily just inputs that caused a crash. Jazzer is able to set the directory but the contents of
96+
these directories are managed entirely by libfuzzer. Unlike with the resources test inputs above, this will not look
97+
in `.cifuzz-corpus/<test class name>` for shared test cases. This is a limitation of libfuzzer.
98+
99+
#### `evaluateExecutionCondition`
100+
101+
This will run once per argument set returned by `provideArguments` for this test. All argument sets will return as
102+
enabled.
103+
104+
#### `interceptTestTemplateMethod`
105+
106+
This will run for each individual test case for each fuzz test and will mostly just allow the test function to proceed
107+
with the provided arguments. Prior to the call to the test, it will enable the agent's hooks and then disable them
108+
afterward. It will also check for and report any findings from Jazzer to JUnit.
109+
110+
### Diagrams
111+
112+
Below are two sequence diagrams for how JUnit calls `evaluateExecutionConditions` and `provideArguments` in fuzzing and
113+
regression mode. These diagrams ignore `interceptTestTemplateMethod` for brevity as its behavior and place in the
114+
sequence is more clear.
115+
116+
#### Fuzzing
117+
118+
![created on sequencediagram.org, load the svg in the editor to edit](./images/fuzzing-flow.svg)
119+
120+
#### Regression
121+
122+
![created on sequencediagram.org, load the svg in the editor to edit](./images/regression-flow.svg)

0 commit comments

Comments
 (0)