Create a Java API Fuzz Test

How to Create Your First Java API Fuzz Test

After the initialization is completed:

it is time to create our first Java API fuzz test. On the left side panel of the Visual Studio Code extension we need to select "Add fuzz test" and then "Java API Fuzz Test".

java-api-select-fuzztest-typeAfterwards a new windows pops up and we need to give the fuzz test a name, specify which JARs of the build step before (Project Initialization) will be necessary for the fuzz test. In addition it is necessary to specify the Instrumentation Filters which will instrument the specified Java classes. This enables to get feedback for the code covered by the fuzz test. 

commons-imaging-create-fuzz-test

Now save the fuzz test. You should see a successful save message.

fuzz_test_creation_pop_up

The fuzz test creation will create two files in .code-intelligence/fuzz_targets, a .java file containing a draft for the fuzz test and a .yaml file containing the fuzz test options. Both files can be modified and updated at any time.

java-api-fuzz-test-creation-file-browser

To write some fuzz test code we can just select the .java file or click on your fuzz test in the left side panel and select "GO TO FILE".

java-api-go-to-file-button

This will directly open the draft for your fuzz test.

java-api-fuzztest-draft

The CallYourAPI() function in line 6 is just a placeholder for the target function to be fuzzed. The input parameter of type byte will be used as fuzzing data for the target function and be mutated after each run. 

Writing Java API fuzz tests is a manual approach and requires to browse the source code of your target application and to select interesting target functions. In the context of the common-image library we are interested in testing image parser functions of the library. The CallYourAPI() function can be replaced by the target function. In addition to the creation and initialization of dependent target function objects we need to import the necessary classes in the fuzz target file. 

The following code shows an example of a possible fuzz test for the getBufferedImage() function of class commons.imaging.formats.jpeg.JpegImageParser:

java-api-fuzztest-final

After fuzz test writing we can execute the fuzz test by clicking on the fuzz test in the Visual Studio plugin on the left side and selecting "RUN".

Your project and fuzz test is build now. After some time you should see some fuzzing run metrics:

java-api-fuzzing-results

If you have trouble building or running the fuzz test because  a jar is missing, check java-corner-cases.

Available functions

A Java fuzz target class needs to define exactly one of the following functions:

  • public static void fuzzerTestOneInput(byte[] input): Ideal for fuzz targets that naturally work on raw byte input (e.g. image parsers).
  • public static void fuzzerTestOneInput(com.code_intelligence.api.FuzzedDataProvider data): A variety of types of "fuzzed data" is made available via the FuzzedDataProvider interface (see below for more information on this interface).

The fuzzer will repeatedly call this function with generated inputs. All unhandled exceptions are caught and reported as errors.

The optional functions public static void fuzzerInitialize() or public static void fuzzerInitialize(String[] args) can be defined if initial setup is required. These functions will be called once before the first call to fuzzerTestOneInput.

The optional function public static void fuzzerTearDown() will be run just before the JVM is shut down.

Fuzzed Data Provider

For most non-trivial fuzz targets it is necessary to further process the byte array passed from the fuzzer, for example to extract multiple values or convert the input into a valid java.lang.String. We provide functionality similar to atheris' FuzzedDataProvider and libFuzzer's FuzzedDataProvider.h to simplify the task of writing JVM fuzz targets.

If the function:

public static void fuzzerTestOneInput(FuzzedDataProvider data)

is defined in the fuzz target, it will be passed an object implementing com.code_intelligence.jazzer.api.FuzzedDataProvider that allows consuming the raw fuzzer input as values of common types. This can look as follows:

package com.example.MySecondFuzzTarget;

import com.code_intelligence.jazzer.api.FuzzedDataProvider;

public class MySecondFuzzTarget {
public static void callApi(int val, String text) {
...
}

public static void fuzzerTestOneInput(FuzzedDataProvider data) {
callApi1(data.consumeInt(), data.consumeRemainingAsString());
}
}

For additional information about FuzzedDataProvider methods, see the javadocs.

It is highly recommended to use FuzzedDataProvider for generating java.lang.String objects inside the fuzz target instead of converting the raw byte array to directly via a String constructor as the FuzzedDataProvider implementation is engineered to minimize copying and generate both valid and invalid ASCII-only and Unicode strings.

Advanced options

Various changes to fuzzing can be made by providing certain command line arguments in the fuzz target .yaml file. They can all be put in run_extra_args. Example:

 

For the list of available options, see the Advanced Options section in the open-source Jazzer documentation.