Jazzer Documentation

repository·main·Indexed 22 days ago

https://github.com/codeintelligencetesting/jazzer

Jazzer is a coverage-guided, in-process fuzzer for the JVM based on libFuzzer. It enables developers to write fuzz tests in Java and Kotlin using JUnit 5 annotations, such as @FuzzTest, to find bugs through automated input mutation. The tool supports integration with Maven, Gradle, and Bazel, and includes built-in sanitizers for detecting security vulnerabilities like SSRF, File Path Traversal, and OS Command Injection.

Tokens
11.7K
Snippets
29
Records
56
Agent score
78%

What's inside Jazzer

  1. What is the Jazzer Mutation Framework?

    main

    The Mutation Framework allows Jazzer fuzz tests to accept multiple parameters of various primitive and object types, rather than being restricted to a single FuzzedDataProvider or byte[].

    Instead of manually parsing bytes to create complex objects, the framework uses type information to directly generate and mutate valid inputs. This makes fuzz tests for complex data structures more efficient and less cumbersome.

    Key Characteristics:

    • Extensible and Composable: Type-specific mutation logic is encapsulated in dedicated mutators that automatically compose for complex types (e.g., a List mutator uses the mutator for its element type).
    • Stability: The framework integrates with the underlying fuzzing engine to ensure that changes to mutation logic do not invalidate existing findings or corpus entries.
    • Automatic Usage: If a fuzz function expects a single FuzzedDataProvider or byte[] parameter, the mutation framework is not used. To trigger the framework, use supported types as parameters.

    The framework is located in the com.code_intelligence.jazzer.mutation package.

    record SimpleTypesRecord(boolean bar, int baz) {
    }
    
    @FuzzTest
    public void testSimpleTypeRecord(SimpleTypesRecord record) {
        doSomethingWithRecord(record);
    }
  2. What is Selffuzz

    main
    Selffuzz is a specialized package containing fuzz tests for Jazzer. It is designed to overcome the constraint that Jazzer cannot instrument its own code. It achieves this by taking the built Jazzer JAR and shading it. This allows normal Jazzer classes to run the fuzzing engine while the test code calls shaded Jazzer classes that have been instrumented.
  3. Understand Jazzer input directories

    main

    Jazzer manages two types of input files:

    Generated corpus directory

    Stores inputs that generate new code coverage during fuzzing. Location: .cifuzz-corpus/<package>.<FuzzTestClass>/<fuzzTestMethod>

    Inputs directory

    Stores inputs that trigger a crash (e.g., uncaught exceptions or sanitizer triggers). Location: src/test/resources/<package>/<FuzzTestClass>Inputs/<fuzzTestMethod> (If this directory doesn't exist, it defaults to the execution directory).

    Tip: If using Git, mark these directories as binary in .gitattributes to avoid corruption:

    src/test/resources/** binary
    .cifuzz-corpus/** binary
  4. Manage Selffuzz dependencies in Maven and Bazel

    main

    Selffuzz uses both Maven and Bazel, but they do not interact.

    • Bazel is used for integration with the wider project's build system and IntelliJ.
    • Maven is used by cifuzz to run the fuzz tests.

    Important: Because these systems are independent, any dependencies used in the tests must be listed in both the Maven and Bazel configuration files.

  5. Apply annotations recursively to nested types

    main

    To apply an annotation to a type and all its nested component types (e.g., ensuring every element in a nested list is non-null), use the constraint property set to PropertyConstraint.RECURSIVE.

    @FuzzTest
    public void fuzz(@NotNull(constraint = PropertyConstraint.RECURSIVE) List<List<Integer>> list) {
        // list is not null and does not contain null entries on any level
        assertDeepNotNull(list);
    }
  6. Seed fuzz tests using JUnit parameter sources

    main

    You can provide initial seed inputs to a @FuzzTest using standard JUnit parameter sources like @MethodSource, @CsvSource, @ValueSource, or custom @ArgumentsSource.

    • In Regression mode: Jazzer executes the test once for each provided argument set (similar to @ParameterizedTest).
    • In Fuzzing mode: The seed inputs are used as the starting point for further mutations.

    Warning: Objects that cannot be serialized (e.g., those mutated by CachedConstructorMutatorFactory without getter functions) will be ignored with a warning.

  7. Support for Constructor-based (Immutable) classes

    main

    Jazzer can mutate classes that build internal state via constructor parameters and do not provide getter methods (e.g., immutable classes).

    class ImmutableClassTest {
    
        static class ImmutableClass {
            private final int bar;
            public ImmutableClass(int foo) {
                this.bar = foo * 2;
            }
            String barAsString() {
                return String.valueOf(bar);
            }
        }
    
        @FuzzTest
        void fuzzImmutableClassFunction(ImmutableClass immutableClass) {
            if (immutableClass != null && "42".equals(immutableClass.barAsString())) {
                throw new RuntimeException("42!");
            }
        }
    }
  8. Precedence of Jazzer configuration settings

    main

    Jazzer configuration settings can be provided through multiple sources. When multiple sources define the same setting, the following order of precedence applies (from lowest to highest):

    1. Default value
    2. META-INF/MANIFEST.MF attribute Jazzer-Some-Opt on the classpath
    3. JAZZER_SOME_OPT environment variable
    4. jazzer.some_opt system property
    5. jazzer.some_opt JUnit configuration parameter (e.g., in resources/junit-platform.properties)
    6. --some_opt CLI parameter (for standalone Jazzer)
  9. Support for JavaBeans

    main

    Jazzer can generate and mutate instances of classes adhering to the JavaBeans Spec. It uses setters, constructors, and getters to pass values to a JavaBean and extract them for corpus serialization.

    Setter-based approach

    Requires a default no-argument constructor and methods following the setXX and getXX/isXX naming convention. Every setter must have a corresponding getter.

    Constructor-based approach

    Requires a constructor with arguments. If multiple constructors exist, the one with the most supported parameters is preferred. To ensure parameter names are available at runtime for mapping, use the javax.annotation.processing.ConstructorProperties annotation to explicitly specify property names.

    public static class FooBean {
        private String foo;
    
        public String getFoo() {
            return foo;
        }
    
        public void setFoo(String foo) {
            this.foo = foo;
        }
    }
    
    @FuzzTest
    public void testFooBean(FooBean fooBean) {
        // ...
    }
  10. Support for the Builder pattern

    main

    Jazzer supports the Builder pattern, commonly used to simplify complex object construction. This includes implementations generated by Lombok's @Builder and @SuperBuilder annotations.

    • Standard Builder: Gathers parameters in a builder and passes them to the target class constructor.
    • Nested Builder: Used when the builder itself is passed into the target class constructor to support nested type hierarchies.
    class SimpleClassFuzzTests {
    
        @Builder
        static class SimpleClass {
            String foo;
            List<Integer> bar;
            boolean baz;
        }
    
        @FuzzTest
        void fuzzSimpleClassFunction(@NotNull SimpleClass simpleClass) {
            someFunctionToFuzz(simpleClass);
        }
    }
  11. What are Sanitizers (Bug Detectors) in Jazzer

    main

    Sanitizers (also called bug detectors) are built-in checks that monitor the program under test for security vulnerabilities during fuzzing. Unlike low-level C/C++ sanitizers that focus on memory errors, Jazzer sanitizers are designed for Java and JVM applications to detect high-level security issues such as:

    • Server-Side Request Forgery (SSRF)
    • File Path Traversal
    • OS Command Injection

    Sanitizers provide feedback to the fuzzer, allowing it to guide input generation toward values more likely to trigger these specific vulnerabilities, making the fuzzing process more efficient.