5.9 KiB
layout | title | parent | grand_parent | nav_order | permalink |
---|---|---|---|---|---|
default | Integrating a Java/JVM project | Setting up a new project | Getting started | 4 | /getting-started/new-project-guide/jvm-lang/ |
Integrating a Java/JVM project
{: .no_toc}
- TOC {:toc}
The process of integrating a project written in Java or any other language running on the Java Virtual Machine (JVM) with OSS-Fuzz is very similar to the general [Setting up a new project]({{ site.baseurl }}/getting-started/new-project-guide/) process. The key specifics of integrating a JVM project are outlined below.
Jazzer
Java fuzzing in OSS-Fuzz depends on Jazzer, which is pre-installed on the OSS-Fuzz base docker images. As Jazzer operates directly on the bytecode level, it can be applied to any project written in a JVM-based language. More information on how Jazzer fuzz targets look like can be found in its README's Usage section.
Project files
Example project
We recommend viewing json-sanitizer as an example of a simple Java-only fuzzing project. Additional examples, including one for a Java project with native dependencies, are part of the java-example project.
project.yaml
The language
attribute must be specified as follows:
language: jvm
The only supported fuzzing engine is libFuzzer (libfuzzer
). So far the only
supported sanitizer is AddressSanitizer (address
), which needs to be
specified explicitly even for pure Java projects.
fuzzing_engines:
- libfuzzer
sanitizers:
- address
Dockerfile
The OSS-Fuzz base Docker images already come with OpenJDK 15 pre-installed. If you need Maven to build your project, you can install it by adding the following line to your Dockerfile:
RUN apt-get update && apt-get install -y maven
Apart from this, you should usually not need to do more than to clone the
project, set a WORKDIR
, and copy any necessary files, or install any
project-specific dependencies here as you normally would.
Fuzzers
In the simplest case, every fuzzer consists of a single Java file with a
filename matching *Fuzzer.java
and no package
directive. An example fuzz
target could thus be a file ExampleFuzzer.java
with contents:
public class ExampleFuzzer {
public static void fuzzerTestOneInput(byte[] input) {
...
// Call a function of the project under test with arguments derived from
// input and throw an exception if something unwanted happens.
...
}
}
build.sh
For JVM projects, build.sh
does need some more significant modifications
over C/C++ projects. Below is an annotated example build script for a
Java-only project with single-file fuzz targets as described above:
# Step 1: Build the project
# Build the project .jar as usual, e.g. using Maven.
mvn package
# In this example, the project is built with Maven, which typically includes the
# project version into the name of the packaged .jar file. The version can be
# obtained as follows:
CURRENT_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate \
-Dexpression=project.version -q -DforceStdout)
# Copy the project .jar into $OUT under a fixed name.
cp "target/sample-project-$CURRENT_VERSION.jar" $OUT/sample-project.jar
# Specify the projects .jar file(s), separated by spaces if there are multiple.
PROJECT_JARS="sample-project.jar"
# Step 2: Build the fuzzers (should not require any changes)
# The classpath at build-time includes the project jars in $OUT as well as the
# Jazzer API.
BUILD_CLASSPATH=$(echo $PROJECT_JARS | xargs printf -- "$OUT/%s:"):$JAZZER_API_PATH
# All .jar and .class files lie in the same directory as the fuzzer at runtime.
RUNTIME_CLASSPATH=$(echo $PROJECT_JARS | xargs printf -- "\$this_dir/%s:"):\$this_dir
for fuzzer in $(find $SRC -name '*Fuzzer.java'); do
fuzzer_basename=$(basename -s .java $fuzzer)
javac -cp $BUILD_CLASSPATH $fuzzer
cp $SRC/$fuzzer_basename.class $OUT/
# Create an execution wrapper that executes Jazzer with the correct arguments.
echo "#!/bin/sh
# LLVMFuzzerTestOneInput for fuzzer detection.
this_dir=\$(dirname \"\$0\")
LD_LIBRARY_PATH=\"$JVM_LD_LIBRARY_PATH\":\$this_dir \
\$this_dir/jazzer_driver --agent_path=\$this_dir/jazzer_agent_deploy.jar \
--cp=$RUNTIME_CLASSPATH \
--target_class=$fuzzer_basename \
--jvm_args=\"-Xmx2048m\" \
\$@" > $OUT/$fuzzer_basename
chmod u+x $OUT/$fuzzer_basename
done
FuzzedDataProvider
Jazzer provides a FuzzedDataProvider
that can simplify the task of creating a
fuzz target by translating the raw input bytes received from the fuzzer into
useful primitive Java types. Its functionality is similar to
FuzzedDataProviders
available in other languages, such as
Python and
C++.
On OSS-Fuzz, the required library is available in the base docker images under
the path $JAZZER_API_PATH
, which is added to the classpath by the example
build script shown above. Locally, the library can be obtained from
Maven Central.
A fuzz target using the FuzzedDataProvider
would look as follows:
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
public class ExampleFuzzer {
public static void fuzzerTestOneInput(FuzzedDataProvider data) {
int number = data.consumeInt();
String string = data.consumeRemainingAsString();
// ...
}
}
For a list of convenience methods offered by FuzzedDataProvider
, consult its
javadocs.