Skip to main content

FAQ

This is a set of frequently asked questions related to fuzzing.

What is fuzzing?

Fuzzing is a powerful software testing technique that involves providing a range of random or semi-random inputs to a target application to identify potential weaknesses, vulnerabilities, or bugs. By systematically generating and modifying test inputs, fuzzing helps to discover issues that may not be easily uncovered through traditional testing methods. The primary goals of fuzzing include improving software security, enhancing application stability, and ensuring the overall robustness of the code.

Memory Unsafe Languages

Fuzzing is particularly effective when applied to memory unsafe languages like C and C++. These languages are more prone to memory-related vulnerabilities, such as buffer overflow, use-after-free, and uninitialized memory usage. Fuzzing can help identify these issues early in the development cycle, enabling developers to fix them before they can be exploited by attackers. Furthermore, the process of fuzzing can reveal complex code paths or edge cases that might be missed by other testing methods. By exposing these hidden flaws, fuzzing helps improve the overall security and stability of applications developed in memory unsafe languages.

Memory Safe Languages

While memory safe languages like Java, JavaScript, Python, or Rust have built-in mechanisms to prevent many common memory-related vulnerabilities, fuzzing remains a valuable testing technique for these languages as well. In memory safe languages, fuzzing can be used to identify logic errors, incorrect assumptions, or other application-specific vulnerabilities that might not be related to memory management. Additionally, fuzzing can uncover issues related to third-party libraries or dependencies, which could introduce security risks or cause crashes. For applications developed in memory safe languages, fuzzing contributes to increased reliability, performance, and security by discovering and allowing developers to address these potential weaknesses.

What goals can I achieve with fuzzing?

Fuzzing is a versatile and effective testing technique that can be applied to a wide range of programming languages and applications. Regardless of whether a language is memory safe or memory unsafe, fuzzing aids in achieving key goals such as strengthening security, enhancing stability, and ensuring robustness. By incorporating fuzzing into the software development process, developers can proactively identify and resolve vulnerabilities, leading to more reliable and secure applications.

What feedback/output/results should I expect from fuzzing?

Fuzzing provides valuable feedback and output in the form of uncovered software vulnerabilities and inconsistencies. As you input a variety of unexpected or random data into your system (the process known as 'fuzzing'), the system responds, often in unforeseen ways. These responses can reveal hidden bugs, exceptions, memory leaks, or even system crashes. Some fuzzing tools offer detailed reports on each fuzzing session, including information on input data that led to unexpected behavior, system status, and potential vulnerabilities discovered.

It's essential to remember that fuzzing does not assure you of the absence of bugs but instead reveals their presence. Fuzzing's strength lies in its capacity to expose unforeseen edge cases that could lead to potential security breaches or system crashes. Interpreting the output from fuzzing requires a solid understanding of the software under test and an analytical mindset to determine the potential impact of the identified issues. In essence, the results from fuzzing provide a roadmap for further investigation and remediation, making your software more secure, stable, and robust.

How long should I run a fuzz test?

Determining the ideal duration for a fuzz test can be challenging, as it depends on various factors such as the complexity of the target application, the resources available, and the specific goals of the testing process. As a general rule of thumb, one can say the longer you fuzz, the higher chance you have to discover bugs and vulnerabilities, at least until your fuzzing campaign is saturated (e.g. not finding any new code coverage anymore). That said, there is no one-size-fits-all answer, understanding some key considerations can help guide you in deciding how long to run your fuzz tests.

Factors influencing fuzzing duration

  • Complexity of the target application - Larger and more complex applications typically require longer fuzzing sessions to effectively explore various code paths and potential vulnerabilities. A more comprehensive testing process will increase the likelihood of discovering hidden issues and ensuring the application’s robustness.

  • Available resources - The amount of time you can allocate to fuzzing will depend on your available resources, such as computing power and personnel. If resources are limited, you may need to prioritize certain components of the application or focus on specific vulnerabilities. You can also consider using distributed fuzzing or cloud-based fuzzing services like CI Sense to scale your testing efforts and reduce the time required.

  • Goals and priorities - Your fuzz testing objectives will influence the duration of the test. If you’re aiming to meet specific security requirements or achieve a certain level of code coverage, you may need to run fuzz tests for a longer duration to achieve those goals. On the other hand, if you’re running fuzz tests as part of a continuous integration (CI) process, shorter, more frequent fuzzing sessions may be more appropriate.

Guidelines for fuzzing duration

As a starting point, consider running individual fuzz tests for at least 24 hours. This will often provide sufficient time to discover many common issues. However, for more critical or complex applications, it’s not uncommon to run fuzz tests for several weeks or even months. Continuously monitoring the progress of your fuzz testing and analyzing the results can help you determine if additional time is needed.

For ongoing development projects, integrating fuzz testing into your CI/CD pipeline can help ensure that newly introduced code is regularly tested. By continuously fuzzing your application, you can proactively identify and fix vulnerabilities before they make their way into production.

The ideal fuzz test duration varies depending on factors such as application complexity, available resources and testing goals. To make the most of your fuzz testing efforts, consider starting with a baseline duration and then adjust as needed based on your finding and priorities. Continuous fuzz testing, when integrated into your development process, can help ensure the ongoing security and stability of your application as fuzz testing is not a one-shot solution.

The metric “execution time without new path“ or “elapsed time after a new path was found“ provides a good indicator of the saturation of a fuzz test. For example, if a fuzz test has been running for three days and no new path has been found for two and a half day, it is unlikely that a new path can be found. This may be because all paths have already been discovered or the fuzz test cannot reach the paths. In the second case, the coverage of the fuzz test should be looked at to improve the fuzz test.

How often should I run a fuzz test?

This question cannot be answered with one general answer either. Many factors play a role in the estimation.

The essential factors are:

  • The magnitude and complexity of the software: The bigger and more complex the software, the more often it should be fuzzed. With each new fuzz run, the coverage can be increased and the likelihood of finding hidden bugs increases.

  • New or old software: Another deciding factor can be whether it is a new software to be developed or an already developed software. With new software, it makes sense to fuzz it often, as it changes and expands frequently. With software that has already been developed, it makes sense to test it when something has changed.

  • Feature frequency: Software projects that require many changes and/or new features in a short period of time should be tested in an aligned manner. Bugs should be found and fixed immediately after the feature has been created. The later they are found, the more effort is required to fix them.

  • Project resources and budget: Another factor are resources and budget. The more it can be fuzzed, the more likely is to find a bug earlier and fix it cheaper than finding it late in the project. Fixing bugs late in the project is more expensive and can lead to additional cost as resources and budget to be reallocated.

  • Skill of the developer: The skill of the development team is a factor that is often neglected. Experienced developers make fewer mistakes than new and inexperienced developers.

Generally, fuzzing can be done at the unit test-, system test level or at least before a major release.

At the unit test level, fuzzing can be integrated into the Continuous Integration pipeline so that, for example, a fuzz run can be triggered with each pull request. The frequency of fuzzing is thus determined by the pull requests. The advantage is that the application is fuzzed often at an early stage and bugs can be fixed quickly and cheaply.

Fuzzing can also be performed at the system test level. At this level, fuzzing can be performed less frequently. But you can fuzz the entire system with it. But the fuzz duration should be significantly longer here. The disadvantage is that it is more time-consuming and expensive to fix a bug at this point.

Fuzzing can be automated to a great extent, so that it can be used as a regression test in many cases.

What is a good coverage percent for a project?

There is no absolute target percentage as it depends much more on the requirements of the project.

Higher code coverage generally indicates that the fuzzer is going through more paths and therefore has a higher likelihood of discovering bugs.

As an example, if we assume that the coverage should be 80%, for a project with 1000 lines of code this would be only 800 lines of code, which is not difficult to achieve. For a project with 1,000,000 lines of code, the coverage would be 800,000 lines of code which is very optimistic and not easily achievable. Therefore, for a project with over a million lines of code, even small percentage of coverage is good coverage.

While this is a useful metric to measure the effectiveness of fuzzing, it shouldn't be the main criteria. Improving the corpus and fuzz target with a more real-word scenario can be much more effective at finding more critical bugs.

How do I improve my code coverage?

To improve code coverage, it is helpful to generate a source line coverage report and investigate the report in detail. Usually the report format can be specified and will enable you to do the analysis in a browser or your favorite IDE. In CI Fuzz, you can do this by running cifuzz coverage <fuzz_test>. There might be various reasons why a specific code part is not covered such as:

  • The executed fuzz test is theoretically unable to reach the code. Write more fuzz tests or adjust the fuzz test to reach new code parts.

  • The fuzzing time was set too short. By default, fuzzing is not deterministic and might have focused on another part of the code during the run. Check the fuzzer statistics and check when the last new path was found. If coverage during the run is still increasing, that is a good indicator to allow the fuzzer to continue to run until coverage plateaus.

  • The software under test expects complex input data where edge cases are never covered by the fuzzer. Value profiling could help the fuzzer to get more precise feedback from the application. If you want to quickly increase the code coverage, it is suggested to add seed corpora and a dictionary for the fuzz test to help the fuzzer to generate syntactic and semantically valid inputs.

  • There is a fuzzing blocker that the fuzzer regularly runs into. This can be an issue in the tested code that the fuzzer re-discover regularly, that needs to be fixed in order to unblock the fuzzer and discover new code parts. However, this can also be an obstacle that the fuzzer is unable to resolve without additional help, such as checksums to verify integrity of inputs. Those obstacles need to be solved either in the fuzz test, by generating valid input that are able to pass checksums or in the software under test by disabling those kind of checks.

How do I know I wrote a good fuzz test?

During the fuzz target writing, we can keep the following things in mind to write a good fuzz target:

  • Determinism: A fuzz target needs to have the same behavior given the same input. A fuzz target shouldn't use any additional source of randomness.

  • Speed: A good fuzz target should be very fast since fuzzing requires many iterations.

  • Memory consumption: For CPU-efficient fuzzing a good fuzz target should consume less RAM than it's available on a given machine per one CPU core.

  • Coverage discoverability: It's often insufficient to have a seed corpus with good code coverage to claim that the fuzzing engine can discover many code paths in the program under test. The seed corpus may give good code coverage, but mutations of the seed corpus may be rejected early. Therefore, it's important to ensure that the fuzz target can discover a large subset of reachable control flow edges without using the seed corpus. If a fuzz target without seed corpus doesn't provide coverage comparable to with seed corpus, consider splitting the fuzz target into smaller targets, using dictionaries or structure-aware fuzzing.

  • I/O: A good fuzz target shouldn't use I/O because it can introduce non-deterministic behavior and make findings harder to reproduce. Avoid debugging output to stderr or stdout as it slows down fuzzing. Avoid reading from a disk other than during initialization. Avoid writing to disk.

There are different ways to evaluate the quality of a fuzz test:

  • Did the fuzz test discover any bugs or vulnerabilities like stack overflow, out of memory, or remote code execution.

  • The coverage and feature metrics of the fuzz test should gradually increase and the execution per second shouldn't be too low.

  • Check the coverage of your targeted modules with tools like JaCoCo after the run. Generally speaking, the higher the coverage the better.

References:

What types of bugs will the fuzzer catch automatically?

Each fuzzer typically has a set of sanitizers or bug-detectors it works with to automatically detect specific types of bugs.

Libfuzzer

Libfuzzer works in conjunction with several sanitizers.

AddressSanitizer is capable of finding various memory related bugs:

  • Out-of-bounds accesses to heap, stack, and globals
  • Use-after-free, use-after-return, use-after-scope
  • Double-free, invalid free
  • Memory leaks (experimental)

UndefineBehaviorSanitizer is used to detect several types of undefined behavior at runtime:

  • Array subscript out of bounds, where the bounds can be statically determined
  • Bitwise shifts that are out of bounds for their data type
  • Dereferencing misaligned or null pointers
  • Signed integer overflow
  • Conversion to, from, or between floating-point types which would overflow the destination

Other sanitizers worth mentioning:

  • ThreadSanitizer (data races)
  • MemorySanitizer (uninitialized reads)
  • LeakSanitizer (memory leaks)
  • DataFlowSanitizer (data flow analyis, no bugs)

Jazzer

Since Java is a memory safe language, there are no sanitizers that focus on memory corruption bugs. Jazzer can detect uncaught exceptions and memory leaks and infinite loops that can lead to DoS attacks. Through its sanitizers Jazzer can also detect a variety of bug classes common to the Java software ecosystem: Deserialization - detects unsafe deserialization that leads to attacker-controlled method calls.

  • Expression Language Injection - detects injectable inputs to an expression language interpreter.
  • LDAP Injection - detects LDAP DN and search filter injections.
  • Naming Context Lookup - detects JNDI lookups such as log4j.
  • Command Injection - detects unsafe execution of OS commands using ProcessBuilder.
  • Reflective Call - detects unsafe calls that lead to attacker-controlled class loading.
  • Regular Expression Injection - detects regex based injection that lead to OOM.
  • Script Engine Injection - detects insecure user input in script engine invocation.
  • Server Side Request Forgery - detects unsafe network connection attempts.
  • SQL Injection - detects SQL injections.
  • XPath Injection - detects XPath injections.

Jazzer.js

Similar to Java the language JavaScript is memory safe. Jazzer.js can detect uncaught exceptions and memory leaks and infinite loops that can lead to DoS attacks. Through its sanitizers Jazzer.js can also detect a variety of bug classes common to the JavaScript software ecosystem:

  • Command Injection - detects unsafe execution of OS commands using ProcessBuilder class.
  • Path Traversal - detects inputs triggering the access of files and directories outside the current one
  • Prototype Pollution - will be out soon!

How do I assess findings?

In general, all findings discovered by a fuzzer can be assessed by analyzing the generated stacktrace and crashing input. For every finding, the fuzzer will generate a crashing input that can be used to reproduce the issue. Additionally, the crashing input can be used as a regression test, after a fix is implemented. Our tools, cifuzz (CLI) and CI Sense (Server) have dedicated features that aid in the assessment of findings.

The cifuzz tool tracks all local findings and let you list these findings with the cifuzz findings command:

Severity | Name             | Description          | Fuzz Test    | Location
9.0 | pretty_star | heap buffer overflow | my_fuzz_test | in exploreMe (src/explore_me.cpp:18:11)
2.0 | wandering_cow | undefined behavior | my_fuzz_test | in exploreMe (src/explore_me.cpp:13:11)

Detailed finding information, including stacktrace, sanitizer details, severity, description & mitigation, can be assessed with cifuzz finding <name>.

CI Sense tool provides similar finding information in the UI, simplifying the process of collaborative review and assessment of finding with a team.

CI Sense Finding Details

Static assessment of finding details is often insufficient and requires additional dynamic analysis. A debugger should be attached during execution of the fuzz target running with the crashing input of the finding. The stacktraces are valuable in the debugging process to identify interesting code positions where it's helpful to place breakpoints and investigate the memory state.

How do I continue fuzzing after a finding?

Sometimes we want the fuzzer to continue running even when a bug is found. In this case we need to use flags to enable this functionality.

For Java, we can use the --keep_going=<numbers of crashes allowed> command line arguments to specify how many findings we allow cifuzz to find before it stops.

This can be set globally for the fuzzing project by inserting this into the file cifuzz.yaml. This will be applied for each local fuzz test

engine-args:
- --keep_going=10

It is also possible to use engine-args in the command line, but here it's engine-arg without s. This only affects the selected fuzz test.

cifuzz run -v com.example.FuzzTestCase --engine-arg --keep_going=10
or
cifuzz remote-run com.example.FuzzTestCase --engine-arg --keep_going=10

For C/C++, we can use the environment variable ASAN_OPTIONS=detect_leaks=0:halt_on_error=0, so that the fuzz run no longer stops at a finding.

Here it can also be set up globally with an environment variable:

export ASAN_OPTIONS=detect_leaks=1:halt_on_error=0

then

cifuzz remote-run my_fuzz_test --env ASAN_OPTIONS=detect_leaks=0:halt_on_error=false

or

cifuzz bundle my_fuzz_test --env ASAN_OPTIONS=detect_leaks=0:halt_on_error=false

By default we stop a local fuzz run after a finding is found. In CI Sense we also do this for Java fuzz test and for C/C++ only for certain finding.

Certain bugs can affect the overall behavior of the App and cause additional bugs that are then false positive.

Which code should I instrument, which code should I ignore?

When it comes to fuzzing, deciding which code to instrument and which code to ignore is critical to maximizing the performance and effectiveness of your fuzzing campaign. Proper instrumentation can lead to better code coverage, more efficient execution, and improved vulnerability discovery.

Code Coverage vs. Bug Detection Instrumentation

When it comes to instrumentation, there are two primary goals: maximizing code coverage and detecting bugs. Most often, these are distinct and are subject to careful considerations.

  • Code Coverage - Instrumentation for code coverage aims to guide the fuzzer in exploring as many code paths as possible within a target application. This is achieved by collecting information about code execution paths, which is then used to generate new inputs that trigger previously unexplored paths. To maximize code coverage, it's essential to instrument critical and complex parts of an application, as well as any custom libraries or components. Depending on the fuzzer and/or configuration of the former, there may exist different granularities of code coverage collection metrics, such as function-, basic block-, or even instruction-based ones. This, with the fuzzed application in mind, can introduce different orders of performance impacts. To minimize this impact, you should prioritize instrumenting the most critical parts of your application first. Also, avoiding instrumentation of widely tested third-party libraries or system libraries can help reduce overhead and improve the overall performance of a fuzzing campaign. However, if you suspect vulnerabilities in third-party libraries, you may choose to instrument them selectively on top of your own application.

  • Finding bugs - To maximize the effectiveness of a fuzzing campaign in discovering vulnerabilities, one can add additional code instrumentation beyond simple code coverage collection. Adding such capabilities is typically helpful, despite them typically introducing a significant performance impact. For C/C++ applications, an example would be AddressSanitizer, which introduces on average a 2x slowdown on its own. This highlights, that pairing multiple bug detectors can add significant slowdown, which partially can negate the advantage fuzzing brings to the table. So, if possible, it’s advisable that when using bug detection tools, one should instrument code that is more likely to contain memory-related vulnerabilities, such as custom memory management implementations or components that handle complex data structures. If such a partial instrumentation is not possible, avoiding stacking multiple bug detectors in a single fuzzing campaign can be beneficial. Instead, one should run multiple fuzzing campaigns either in parallel or in sequence, each with a different bug detector instrumentation.

Deciding which code to instrument and which code to ignore during a fuzzing campaign is crucial to optimizing performance and vulnerability discovery. By focusing on instrumenting critical components, security-sensitive code, and areas prone to memory-related issues, you can maximize the effectiveness of your fuzzing efforts. Balancing the goals of code coverage and bug detection, along with being mindful of the impact on execution performance, will help ensure a successful fuzzing campaign. Code coverage instrumentation likely always has a higher prioritization than specific bug detection capabilities, as without the former the chance of hitting deep code paths with a fuzzer is minimized and therefore those code areas, which were instrumented with a bug detector may never be reached. Finally, this can be a very iterative approach, as there's no universal solution that applies to every project out there.

What are dictionaries?

Dictionaries are files containing lists of tokens that can be utilized by the fuzzing engine to generate novel test inputs. Examples of Dictionaries for various input types and formats can be found here.

When should I use dictionaries?

Dictionaries are useful when the code being fuzzed expects certain keywords, formats or syntax for the fuzzed input during processing. By providing a Dictionary you may improve the speed at which the fuzzing engine generates new inputs that trigger new code paths during execution.

What is a corpus?

A corpus is a set of test input values that are known to trigger code paths in the target code. These come in two primary forms:

  • Seed corpus: A small corpus hand-crafted or generated to trigger initial code path execution at the first time when a fuzz test is running. If you're fuzzing a well known protocol, any sample data from real world examples can be helpful to assist the fuzzer in quickly generating syntactic and symantic valid input data. A number of corpora can be found here for various data formats.

  • Test or fuzzing corpus: In addition the seed corpus, when the fuzzing engine generates a new input and it detects this input leads to new code paths, this input is saved for further mutation and to be used by the fuzzing engine on future runs. At the beginning of every fuzz test the test corpus is re-evaluated to check which inputs are still leading to unique code paths and only those are taken into account for mutations in the run.

When should I use existing corpora?

Using existing corpora is recommended whenever corpora relevant to an input under test are available. These could be the previously mentioned hand-crafted seed corpus, real world application data, or known corpora from public resources. Corpora can help the fuzzer to quickly increase code coverage and enable the fuzzer reach deep application states.

One caveat with corpora is that it may slow down the fuzzing process when incorrectly selected or tuned corpora are utilized. If the inputs from the corpus doesn’t lead to triggering unique code paths, then these executions are “wasted”. The process of tuning the corpora to only those input that lead to unique paths is known as “corpus minimization”.

How do I mock/replace functions?

When fuzz testing projects, it may be necessary to mock or replace functions. This is often the case in unit testing as well. There are two main reasons for mocking functions:

  1. The software under test (SUT) depends on external functions from other software that are outside the scope of our fuzzing setup. In this scenario, we need to create the definition of those functions to execute the target.

  2. The software under test contains functions that we cannot or do not want to execute in their original form when fuzzing. For example, some functions may have specific hardware dependencies, such as a particular memory layout, that are not satisfied on the hardware where the fuzz test will be executed, or others just degrade the overall fuzzing performance because they have time intensive functionalities, like in the worst case this could be a call to sleep(). In this case, we need to replace the existing functions.

Creating Mocks for Fuzz Testing

Starting with a simple approach and improving over time (e.g. while the fuzz test is running) is often the best way to create mocks for fuzz testing. One way to create mocks is to define a function that always returns a default value, such as 0. If this method does not achieve the desired goals, more sophisticated mocks may be necessary, like mocks that instead of returning fixed values also return data taken from the input generated by the fuzzer.

Replacing Functions in C/C++ projects:

To replace already existing function definitions without getting compiler or linker errors (e.g. “multiple definition of …”) multiple techniques can be used. The appropriate approach depends on the type of the function that needs to be replaced. Common techniques that do not require any changes to the SUT code are:

  • Wrapping functions:
    • Works well for statically linked (not dynamically linked, like .so libraries) functions that are called from other files than the one where they are defined
      • To wrap a function, linker flags need to be added to the project
      • When working with C++ the function mangling can be a bit confusing
      • Allows to call the original function
  • LD_PRELOADing functions:
    • Works well for dynamically linked functions
    • Requires the LD_PRELOAD environment variable to be set when executing the fuzz test
    • Allows to call the original function
  • Overwriting functions:
    • Works well for statically linked (not dynamically linked, like .so libraries) functions even if they are called from within the same file
    • Requires that the symbols of the functions that will be replaced are weak (this can be achieved with tools like objdump)
    • The original function cannot be called

If none of the above techniques are applicable, as a last resort, the code of the software under test can be changed to allow fuzz testing it.

How do I use FuzzedDataProvider?

By default, the fuzzer provides an array of raw bytes to the fuzz test. It would be up to the fuzz test creator to allocate and convert the byte array as needed. The FuzzedDataProvider (FDP) is a class that provides the ability to easily split the input data generated by the fuzzer. This is useful whenever you need to easily generate different data types (strings, ints, floats, etc…) as part of your fuzz test. An FDP is provided for multiple languages / fuzzers.

FDP Data Types

All implementations of the FDP provide similar convenience functions for most common data types (strings, ints, floats, etc...). The methods come in a few varieties:

  • If you need a single value, you can use methods like ConsumeBool, ConsumeIntegral, ConsumeFloatingPoint. Many of these methods will allow you to specify a range of valid values as well.

  • If you need a sequence of bytes, you can use methods like ConsumeBytes, ConsumeBytesAsString.

  • There are also methods that include the word remaining. ConsumeRemainingBytesAsString will return a string from the bytes remaining in the fuzzing input buffer. When using one of these methods, it should be the last FDP method you call in the fuzz test, since there will be no data left in the buffer after the call.

  • If you need to pick a random value from an array, you can use PickValueInArray methods.

C++

The FDP for C++ is a single-header library that is included as part of LLVM. It can be included in your fuzz test with: #include <fuzzer/FuzzedDataProvider.h>. To create the FDP object, you will need to pass it the raw fuzzer input buffer and size. Below is an example of including, creating, and using an FDP object:

#include "src/explore_me.h"
#include <cifuzz/cifuzz.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <stdio.h>

FUZZ_TEST_SETUP() {}

FUZZ_TEST(const uint8_t *data, size_t size) {

//create FuzzedDataProvider object from fuzzer input buffer and size
FuzzedDataProvider fuzzed_data(data, size);
//create an int from FDP
int a = fuzzed_data.ConsumeIntegral<int>();
//create a 2nd int from FDP
int b = fuzzed_data.ConsumeIntegral<int>();
//generate a string from the remaining bytes in the input buffer
std::string c = fuzzed_data.ConsumeRemainingBytesAsString();

exploreMe(a, b, c);
}

Java

The FDP for Java is part of Jazzer. You can find the Javadocs for FDP here. To use the FDP, just import it in your fuzz test and create a method that accepts an FDP as an argument:

package com.example;

import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.code_intelligence.jazzer.junit.FuzzTest;

public class FuzzTestCase {
@FuzzTest
void myFuzzTest(FuzzedDataProvider data) {
//create an int from FDP
int b = data.consumeInt();
//generate a string from the remaining bytes in the input buffer
String c = data.consumeRemainingAsString();

ExploreMe ex = new ExploreMe(a);
ex.exploreMe(b, c);
}
}

JavaScript

Jazzer.js provides an implementation of FuzzedDataProvider.

To use the FDP, just import it and create a function that accepts the raw fuzzer input. Create a FuzzedDataProvider object from the raw fuzzer input.

const { FuzzedDataProvider } = require("@jazzer.js/core");
/**
* @param { Buffer } fuzzerInputData
*/
module.exports.fuzz = function (fuzzerInputData) {
const data = new FuzzedDataProvider(fuzzerInputData);
//create a string with max length between 10-15 and utf-8 encoding
const s1 = data.consumeString(data.consumeIntegralInRange(10, 15), "utf-8");
//consume 1 byte to create an unsigned integer
const i1 = data.consumeIntegral(1);
//consume 2 bytes to create an unsigned integer
const i2 = data.consumeIntegral(2);
//consume 4 bytes to create an unsigned integer
let i3 = data.consumeIntegral(4);

if (i3 === 1000) {
if (s1 === "Hello World!") {
if (i1 === 3) {
if (i2 === 3) {
throw new Error("Crash!");
}
}
}
}
};