Skip to content

Latest commit

 

History

History
87 lines (61 loc) · 3.49 KB

architecture.md

File metadata and controls

87 lines (61 loc) · 3.49 KB

Architecture

Constraints

Main focus lies on fuzzing Node.js libraries and backend applications using libFuzzer.

Support for:

  • Linux x86_64
  • macOS x86_64 and arm64
  • Windows x86_64

Out of scope:

  • Other runtimes or browser support

Components and Interactions

From an architectural view the fuzzer consists of multiple components and also needs user provided parts. These components and their interactions will be discussed in this section.

The fuzzing library is the main entry point and is used to start fuzzing. It is invoked with the name of a fuzz target module and possible fuzzer options. As a first step it loads a native plugin Node.js addon, fuzzer plugin, to interact with libFuzzer and registers a require hook, interceptor, to instrument subsequently loaded code.

The interceptor transforms code using Babel to provide feedback to the fuzzer in multiple ways. It extends the loaded code to gather coverage statistics so that the fuzzer can detect when new code paths are reached. And it also intercepts comparison functions, like == or !=, to detect the parts of the provided input that should be mutated and in what way. Available feedback methods are defined in libFuzzer's hook interface definitions.

After the initial setup fuzzer library loads the fuzz target, a module exporting a fuzz function, which in turn has to call the actual code under test using the provided fuzzing data. Note that the loaded code in instrumented by instrumentor and will provide fuzzer feedback.

During the actual fuzzing run the fuzzer repeatedly invokes the fuzz function and mutates the provided data based on feedback from the instrumented code.

Once a crash is found it logs it on the console and stops.

Components

Decisions

Fuzzer integration

Context: To interact with the fuzzer there needs to be a way to initialize libFuzzer, receive fuzzing data in the fuzz target and invoke the fuzzer's callback functions. Possible solutions could be a direct integration into the V8 runtime or using a Node.js based approach.

Decision: A Node.js based approach seems to be sufficient for our purpose. Native plugins provide a stable ABI to interact with V8, JavaScript code and vice versa. We will use that as base for the fuzzer.

Consequences: No other runtime can be supported, this includes browsers.

Instrumentation

Context: libFuzzer is a coverage-based fuzzer and hence needs coverage information to detect progress and comparison hints to improve mutations. These feedback mechanisms have to be added to the application code without user intervention.

Decision: Use the istanbul-lib-hook library to hook into dynamic code loading and babel plugins to extend the loaded code with the required feedback functionality. Instrumenting the code at such a high level leads to an independence of the underlying JavaScript engine. Furthermore, it is easily possible to decide if a loaded module should be instrumented or not.

Consequences: Independence of JavaScript engine, fine-grained instrumentation control

Visualization

The most crucial inner mechanisms that make Jazzer.js work are highlighted in the below Figure:

DetailOvervoew