Use this skill to fuzz open source JVM projects (Java, Kotlin, Scala, etc.) using Jazzer.
This skill provides the agent with the knowledge and tools to write, build, and validate fuzz targets for JVM-based projects (Java, Kotlin, Scala, Groovy) integrated into OSS-Fuzz. JVM fuzzing uses Jazzer, which wraps libFuzzer and instruments JVM bytecode for coverage guidance.
JVM projects must use the JVM base builder image:
FROM gcr.io/oss-fuzz-base/base-builder-jvm
Set language: jvm in project.yaml.
The simplest Jazzer harness receives raw bytes via fuzzerTestOneInput:
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
public class MyTargetFuzzer {
public static void fuzzerTestOneInput(byte[] data) {
try {
MyLibrary.parse(data);
} catch (ExpectedExceptionType e) {
// Ignore expected exceptions; they are not bugs.
}
}
}
FuzzedDataProvider splits the raw byte stream into typed values, which is
essential for targets that require structured input:
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
public class MyTargetFuzzer {
public static void fuzzerTestOneInput(FuzzedDataProvider data) {
String header = data.consumeString(64);
int version = data.consumeInt();
byte[] payload = data.consumeRemainingAsBytes();
try {
MyLibrary.process(header, version, payload);
} catch (IllegalArgumentException | IOException e) {
// Expected — not a finding.
}
}
}
Useful FuzzedDataProvider methods:
| Method | Description |
|---|---|
consumeBytes(n) | byte[] of length n |
consumeRemainingAsBytes() | all remaining bytes |
consumeString(maxLen) | arbitrary String |
consumeAsciiString(maxLen) | ASCII-only String |
consumeInt() / consumeInt(min, max) | int |
consumeLong() | long |
consumeBoolean() | boolean |
consumeDouble() | double |
pickValue(collection) | random element |
fuzzerInitializeIf initialisation is expensive (loading config, creating DB connections, etc.), put it in an optional static method that Jazzer calls once before fuzzing:
public class MyTargetFuzzer {
private static MyClient client;
public static void fuzzerInitialize() {
client = new MyClient(/* static config */);
}
public static void fuzzerTestOneInput(FuzzedDataProvider data) {
client.process(data.consumeRemainingAsBytes());
}
}
The build.sh pattern for Maven projects:
# Build the project JARs.
$MVN package -DskipTests -Dmaven.javadoc.skip=true
# Collect JARs needed at runtime.
ALL_JARS="mylib-1.0.jar"
BUILD_CLASSPATH=$(echo $ALL_JARS | xargs printf -- "$OUT/%s:"):$JAZZER_API_PATH
RUNTIME_CLASSPATH=$(echo $ALL_JARS | xargs printf -- "\$this_dir/%s:"):\$this_dir
for fuzzer in $(find $SRC -maxdepth 1 -name '*Fuzzer.java'); do
fuzzer_basename=$(basename -s .java "$fuzzer")
javac -cp $BUILD_CLASSPATH "$fuzzer"
cp $SRC/$fuzzer_basename.class $OUT/
# Wrapper script that launches jazzer_driver with the right arguments.
echo "#!/bin/bash
this_dir=\$(dirname \"\$0\")
if [[ \"\$@\" =~ (^| )-runs=[0-9]+($| ) ]]; then
mem_settings='-Xmx1900m:-Xss900k'
else
mem_settings='-Xmx2048m:-Xss1024k'
fi
LD_LIBRARY_PATH=\"$JVM_LD_LIBRARY_PATH\":\$this_dir \\
\$this_dir/jazzer_driver --agent_path=\$this_dir/jazzer_agent_deploy.jar \\
--instrumentation_includes=com.example.** \\
--cp=$RUNTIME_CLASSPATH \\
--target_class=$fuzzer_basename \\
--jvm_args=\"\$mem_settings\" \\
\$@" > $OUT/$fuzzer_basename
chmod u+x $OUT/$fuzzer_basename
done
For Gradle projects replace $MVN package with the appropriate Gradle command
and adjust JAR paths accordingly.
$OUT/<fuzzer_name>_seed_corpus.zip.$OUT/<fuzzer_name>.dict.try/catch for all
documented exception types. Only unexpected exceptions and crashes are
findings.FuzzedDataProvider for structured input rather than passing raw
bytes to methods that expect well-formed data.fuzzerInitialize: client connections,
parsers with complex configuration, and loaded schemas should be set up once.Math.random(), no System.currentTimeMillis()
in the fuzzing path, no thread spawning.--instrumentation_includes to the package prefix of the target
library in the wrapper script — without this Jazzer cannot guide fuzzing.mem_settings pattern
shown above to avoid OOM kills during runs vs. crash reproduction.OutOfMemoryError, StackOverflowError, and
NullPointerException on invalid input are usually expected — decide which
are genuine bugs for this project.NullPointerException, ArrayIndexOutOfBoundsException,
ClassCastException, NumberFormatException on paths that should not throw.Jazzer can also detect:
Runtime.exec hooks)python3 infra/helper.py build_fuzzers <project>
python3 infra/helper.py check_build <project>
python3 infra/helper.py run_fuzzer <project> <fuzzer_name> -- -max_total_time=30
check_build output carefully.mvn package or
gradle build) to ensure the project itself compiles cleanly.RUN git clone to COPY to avoid network round-trips.