diff options
author | Mark <mteffeteller@google.com> | 2023-06-21 23:30:18 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-06-21 23:30:18 +0000 |
commit | 54819157eaa66e14f2c68b54609bd6bfa360b708 (patch) | |
tree | 68cf332a40b94b2d28b256b19b916f99220bb0c4 /docs/junit-integration.md | |
parent | ba37c2e361c2ba91bacc47fcae5383c52e50f6be (diff) | |
parent | e73be1680dae58cb83d869104def1c59102d59b2 (diff) | |
download | jazzer-api-54819157eaa66e14f2c68b54609bd6bfa360b708.tar.gz |
Sync jazzer in AOSP with upstream repo (new SHA: 30decf81a147c66fa5a098072c38ab6924ba0aa6) am: 9350e0ab03 am: 99d9a79746 am: 34a8e5c8aa am: e73be1680d
Original change: https://android-review.googlesource.com/c/platform/external/jazzer-api/+/2627336
Change-Id: I1b97ed5cdcf2adda4d443148cc0d447974e51785
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
Diffstat (limited to 'docs/junit-integration.md')
-rw-r--r-- | docs/junit-integration.md | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/docs/junit-integration.md b/docs/junit-integration.md new file mode 100644 index 00000000..517f7ab6 --- /dev/null +++ b/docs/junit-integration.md @@ -0,0 +1,111 @@ +# JUnit Integration Implementation + +Jazzer's JUnit integration starts from +the [`FuzzTest`](../src/main/java/com/code_intelligence/jazzer/junit/FuzzTest.java) annotation. As mentioned in the +annotation's javadoc, our integration runs in one of two modes: fuzzing and regression. Fuzzing mode will generate new +inputs to feed into the tests to find new issues and regression mode will run the tests against previous findings, no +fuzzing is done. The main entrypoints for the actual integration code are found in two of the annotations +on `FuzzTest`: `@ArgumentsSource(FuzzTestArgumentsProvider.class)` and `@ExtendsWith(FuzzTestExtensions.class)`. + +Because these same files and functions are involved in two mostly separate sets of functionality, this will look at the +flow of the different methods involved in integrating with JUnit in fuzzing mode (when `JAZZER_FUZZ` is set to any +non-empty value) and in regression mode (when `JAZZER_FUZZ` is not set) separately. + +# Fuzzing Flow + +JUnit will call the following methods for each test marked with `FuzzTest`. + +## `evaluateExecutionCondition` + +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 +be run due to global state in libfuzzer that would mean multiple tests would interfere with each other. Jazzer will +accept the first fuzz test that is checked as the test to be run. It will cache which test it has seen first and +return that test as enabled. + +If this returns that a test is disabled, JUnit will not run the rest of these methods for this test and instead skip +to the next one. + +## `provideArguments` + +This will configure the fuzzing agent to set up code instrumentation, instantiate a `FuzzTestExecutor` and put it into +JUnit's `extensionContext`, then create a stream of a single empty argument set. As the comment mentions, this is so +that JUnit will actually execute the test but the argument will not be used. + +## `evaluateExecutionCondition` + +This will be called for each argument set for the current test. In fuzzing mode, there will only be the single +empty argument set which will be enabled. + +## `interceptTestTemplateMethod` + +This will call `invocation.skip()` which prevents invoking the test function with the default set of +arguments `provideArguments` created. It will instead extract the `FuzzTestExecutor` instance from +the `extensionContext` and then calls `FuzzTestExecutor#execute` which creates a `FuzzTargetRunner` to run the actual +fuzzing. + +Crashes are saved in `resources/<package>/<test file name>Inputs/<test method name>` and results that are interesting to +libfuzzer are saved in `.cifuzz-corpus`. + +# Regression Flow + +Similar to fuzzing mode, JUnit will call these methods for each test marked with `FuzzTest`. + +## `evaluateExecutionCondition` + +This checks if the given test should be run at all. In regression mode, all tests are run so this will always return +enabled. + +## `provideArguments` + +This will configure the fuzzing agent as in fuzzing mode, then gather test cases to run from the following sources: + +1. A default argument set of just an empty input +2. A set of arguments from the associated resources directory +3. If a `.cifuzz-corpus` directory exists, relevant entries from that are added as well + +Prior to returning, the stream of test cases is put through `adaptInputsForFuzzTest` to turn the raw bytes from the +files into the actual types to be given to the tested function. + +### Resources Tests + +The tests from the resources directory are gathered by `walkInputs`. This will look for inputs in two places: +- `resources/<package>/<test class name>Inputs` - files found directly within this directory will be used as inputs for + any tests within this class. This allows for easy sharing of corpus entries. Jazzer does not automatically put entries + here, instead a human will need to decide a finding should be shared and manually move it. +- `resources/<package>/<test class name>Inputs/<test method name>` - files found in this directory and any directory + under it are used as inputs for only the test of the same name. + +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 +search with the given directory in the jar. + +### CIFuzz Corpus + +The corpus kept in `.cifuzz-corpus/<test class name>/<test method name>` holds any inputs that libfuzzer found worth +saving and not necessarily just inputs that caused a crash. Jazzer is able to set the directory but the contents of +these directories are managed entirely by libfuzzer. Unlike with the resources test inputs above, this will not look +in `.cifuzz-corpus/<test class name>` for shared test cases. This is a limitation of libfuzzer. + +## `evaluateExecutionCondition` + +This will run once per argument set returned by `provideArguments` for this test. All argument sets will return as +enabled. + +## `interceptTestTemplateMethod` + +This will run for each individual test case for each fuzz test and will mostly just allow the test function to proceed +with the provided arguments. Prior to the call to the test, it will enable the agent's hooks and then disable them +afterward. It will also check for and report any findings from Jazzer to JUnit. + +# Diagrams + +Below are two sequence diagrams for how JUnit calls `evaluateExecutionConditions` and `provideArguments` in fuzzing and +regression mode. These diagrams ignore `interceptTestTemplateMethod` for brevity as its behavior and place in the +sequence is more clear. + +## Fuzzing + +![created on sequencediagram.org, load the svg in the editor to edit](./images/fuzzing-flow.svg) + +## Regression + +![created on sequencediagram.org, load the svg in the editor to edit](./images/regression-flow.svg) |