
In the past, it was really difficult to fuzz JavaScript engines (those that enable you to add Snowfall to your browser or develop Node.js backends). Mutations in JS code caused syntax errors, which slowed down the process significantly. Samples were discarded by the engine, and you had to generate new ones. Over time, grammar-based fuzzers came to help, but they are not easy-to-use either.
In 2019, security researcher saelo unveiled his project – Fuzzilli. The idea of this fuzzer was to generate bytecode-like output (instead of JavaScript) that would be easier to mutate. The fuzzer name refers to a pasta variety, but, in fact, it originates from FuzzIL (Fuzzing Intermediate Language).

Test system
First of all, you’ll need a Linux virtual machine. A ready-made VM with your favorite distribution can be downloaded from osboxes.org. In my tests, I will use Ubuntu 22.
The more resources you allocate to the test VM, the better. The fuzzer shows code coverage, and, depending on the computation power, fuzzing the entire JS engine can take from a single day to several weeks.
The accessory tools include Git, the Swift programming language (not to be confused with the songstress), and the toolchain required to build JS engines (to be addressed in more detail later).
Start the VM and enter your password.
Password=osboxes.org
echo $Password | sudo -S apt update
sudo apt upgrade -y

Fuzzers
Fuzzing is a testing technique: incorrect, unexpected, or randomized data are fed to a software product as input, and the fuzzer monitors it for crashes, assertion triggers, and memory leaks.
An important fuzzing parameter is code coverage. In essence, this is the proportion of program code covered in the course of a certain set of tests.
Fuzzing can be either ‘dumb’ (or unstructured) or ‘smart’ (i.e. structured). If the fuzzer has no idea about the structure of input data expected by the tested program, this is dumb fuzzing. If it knows this structure, this is smart fuzzing.
Input data can be created using generation or mutation. Generation creates data from scratch; while mutation modifies existing data.
Depending on your knowledge about the source code, fuzzers can be divided into black-box and white-box ones.
Black-box fuzzing implies that you have no information about the program structure; accordingly, the fuzzer creates randomized input data.
White-box fuzzing involves program analysis with the purpose to improve code coverage (e.g. symbolic execution to bypass various parts of the program). However, program analysis takes longer than black-box fuzzing.
In addition, the tested ‘box’ can be gray. In such a case, code instrumentation is used instead of program analysis. This makes it possible to collect information about the program without analysis (i.e. something between a white and a black box). Input data are generated faster, and concurrently you receive code coverage information.
Fuzzilli belongs to the third type. In terms of input data creation, it combines generation and mutation. In terms of fuzzing technology, it’s rather ‘smart’.
JavaScript engines
The main components of a JavaScript engine are parser, interpreter, and compiler.

First, JavaScript source code is parsed. An abstract syntax tree (AST) is built, and bytecode is created on its basis. Then the interpreter executes the bytecode.
During the execution, various information is recorded (data profiling) and subsequently used when the bytecode is compiled into machine code. This operation is performed by the compiler.
Machine code is generated in situations when some code section is used frequently (e.g. a function is executed in a loop). Usually, it makes sense to spend time on its compilation and subsequently gain in execution time (since interpretation is always slower).
Typically, several compilers are used, and the optimal one is selected depending on the code optimization level.
www
More information on JS engines is available in the presentation “JavaScript engines: The Good Parts” (PDF, WebArchive).
Preparing fuzzer
Fuzzilli is available in the form of source code written in Swift.
To download the source code, you’ll need Git; to build Fuzzilli, GCC and Binutils packages (and, of course, the fuzzer source code). Install the dependencies and clone the Fuzzilli repository.
Password=osboxes.org
echo $Password | sudo -S apt update
sudo apt install git binutils gcc -ygit clone https://github.com/googleprojectzero/fuzzilli

Now go to the Swift website and look for a release suitable for your distribution in the Download section. For Ubuntu 22, download the release Ubuntu 22.04 x86_64.

Unpack the archive and copy the usr
folder to install Swift. After that, make sure everything is configured correctly.
Below is a mini-script for lazy hackers. If you are reading this article many years later, replace the SwiftUrl
variable with the respective URL from the Swift page.
# Installing SwiftPassword=osboxes.org
SwiftUrl=https://download.swift.org/swift-5.8.1-release/ubuntu2204/swift-5.8.1-RELEASE/swift-5.8.1-RELEASE-ubuntu22.04.tar.gz
SwiftTar=$(echo $SwiftUrl | sed 's:.*/::')SwiftFolder=${SwiftTar%.tar.gz}# Go to HOME directorycd $HOME# Download archivewget $SwiftUrl# Extracttar -xzf $SwiftTar# Installecho $Password | sudo -S cp -r $SwiftFolder/usr /
# Delete archiverm $SwiftTar# Delete folderrm -rf $SwiftFolder# Test runswift --version

Now go to the Fuzzilli folder and build the fuzzer.
cd fuzzilli && swift build -c release

The fuzzer is ready. You can read its Help section if necessary.
swift run -c release FuzzilliCli --help
Time to prepare JS engines. The instruction on the repository main page says: “Download the source code for one of the supported JavaScript engines. See the Targets/ directory for the list of supported JavaScript engines”. For each engine, there is a separate folder containing an instruction that specifies how to build this particular engine for fuzzing.
Building and fuzzing V8
Theory
Let’s start with the Google Chrome browser engine called V8. It’s a JavaScript and WebAssembly open-source engine developed by Google and written in C++. It’s used in Chrome, Node.js, and numerous Chrome derivatives.
This engine was developed in Aarhus, Denmark, and its lead developer is Lars Bak. Bak was also involved in the development of the Self language and HotSpot Java virtual machine. Therefore, many of the Self developments migrated to V8 (e.g. JIT compilation and object maps).
www
For more detail, see the paper that formulates the basis of Self: “An Efficient Implementation of SELF, a Dynamically-Typed Object-Oriented Language Based on Prototypes” (PDF, WebArchive).
The engine includes the following components: Ignition interpreter, non-optimizing JavaScript compiler, and TurboFan optimizing compiler.

Building V8
The explanatory note in the Targets/V8 folder suggests to follow the instructions provided at https://v8.dev/docs/build. But I compiled all the required steps into a mini-script.
# Preparing and building V8Password=osboxes.org
echo $Password | sudo -S apt update
# Go to HOME directorycd $HOME# Download depot_tools repositorygit clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
# Add to PATHecho "PATH=$HOME/depot_tools:$PATH" >> ~/.bashrc
source ~/.bashrc
# Download source codegclient
fetch v8
cd v8/
gclient sync# Install build dependencies (in fact, update since the script asks to enter password)echo $Password | sudo -S apt update
./build/install-build-deps.sh
# Build V8 using script in the fuzzer folder$HOME/fuzzilli/Targets/V8/fuzzbuild.sh

The script goes to the user home directory, installs depot_tools
(to download and build V8), and downloads and compiles the V8 source code. This process can take some time depending on your PC’s horsepower.
Fuzzing
To start fuzzing, run the following command:
# Run Fuzzilli# Disable core dumpsecho $Password | sudo -S sysctl -w 'kernel.core_pattern=|/bin/false'# Go to Fuzzilli foldercd $HOME/fuzzilli
# Runsswift run -c release FuzzilliCli --profile=v8 --resume --storagePath=$HOME/fuzzilli-storage-v8 $HOME/v8/out/fuzzbuild/d8

The main settings are as follows: engine profile (--profile=v8
), resume the previous fuzzing session (--resume
), and path to the storage where the fuzzer will save its data (--storagePath
): found crashes, sample corpus, and other information.
Fuzzing statistics will be displayed on a regular basis. The most interesting parameters are Crashes Found and Coverage.

Building and fuzzing SpiderMonkey
Theory
Let’s move on to Firefox JavaScript engine.
SpiderMonkey originates from the world’s first JavaScript engine released back in 1995. Originally developed by Brendan Eich at Netscape, its first versions were written in C, but later the code was rewritten in C++.
The SpiderMonkey structure is shown below. The parser produces bytecode. The JavaScript interpreter executes this bytecode. The baseline interpreter creates inline cache. The baseline compiler creates non-optimized machine code, and WarpMonkey creates optimized machine code.

Building SpiderMonkey
The instruction suggests to clone the Gecko repository and run the fuzzbuild.sh script.
But in fact, you have to apply patches from the Patches folder first – and only then build the engine. Also, fuzzbuild should be run not from js/
, but from the gecko-dev
root.

To build the engine, you’ll need curl and the Rust compiler.
# Preparing and building SpiderMonkeyPassword=osboxes.org
cd $HOME# Install curlecho $Password | sudo -S apt install curl -y# Install Rustcurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o install.sh
sh ./install.sh -ysource "$HOME/.cargo/env"# Clone repositorygit clone https://github.com/mozilla/gecko-dev.git
# Go to foldercd gecko-dev/js/src
# Apply patchgit apply $HOME/fuzzilli/Targets/Spidermonkey/Patches/*cd $HOME/gecko-dev
# Build$HOME/fuzzilli/Targets/Spidermonkey/fuzzbuild.sh


Fuzzing
After building the engine, you can start fuzzing it from the fuzzilli
folder. The startup commands and fuzzer parameters are the same, except for the profile and executable file.
# Starting Fuzzilli# Disable core dumpsecho $Password | sudo -S sysctl -w 'kernel.core_pattern=|/bin/false'# Go to Fuzzilli foldercd $HOME/fuzzilli
# Runswift run -c release FuzzilliCli --profile=spidermonkey --resume --storagePath=$HOME/fuzzilli-storage-sm $HOME/gecko-dev/obj-fuzzbuild/dist/bin/js

Building and fuzzing JavaScriptCore
And finally, the JavaScriptCore engine that powers the Safari browser.
Theory
JavaScriptCore originates from the KJS JavaScript engine used in the Konqueror browser, which is part of KDE. The WebKit project started in 2001 as a fork of KHTML and KJS.
It features the most complex four-level pipeline:
- LLInt (Low Level Interpreter) interprets bytecode generated based on the JavaScript source code;
- Then slightly optimizing Baseline compiler goes into play. Both of them collect information required for further machine code optimizations.
- The next component is DFG JIT (Data Flow Graph Just In Time). It enhances machine code optimization; and
- Finally, FTL JIT (Faster Than Light) generates the most optimized machine code.

Building JavaScriptCore
Let’s see what the instruction in the Targets folder says about JavaScriptCore.

You have to clone the code from the WebKit mirror, apply patches, and run the fuzzbuild.sh script. Also, you need to install Clang and dependencies. The Tools
folder contains ready-made scripts for this. Download and install all the required stuff, apply patches, and build the engine. As usual, below is the script:
# Preparing and building JavaScriptCorePassword=osboxes.org
cd $HOME# Download source codegit clone https://github.com/WebKit/WebKit.git
# Install dependenciescd WebKit
echo $Password | sudo -S apt update
sudo apt install clang -yTools/gtk/install-dependencies
# Apply patchesgit apply ../fuzzilli/Targets/JavaScriptCore/Patches/*# Build JSC$HOME/fuzzilli/Targets/JavaScriptCore/fuzzbuild.sh

Fuzzing
After building the engine, you can proceed to fuzzing.
# Running Fuzzilli# Disable core dumpsecho $Password | sudo -S sysctl -w 'kernel.core_pattern=|/bin/false'# Go to Fuzzilli foldercd $HOME/fuzzilli
# Start fuzzingswift run -c release FuzzilliCli --profile=jsc --resume --storagePath=$HOME/fuzzilli-storage-jsc $HOME/WebKit/FuzzBuild/Debug/bin/jsc

Conclusions
Now you are familiar with the fuzzing theory and have a fuzzing platform suitable for the three main JavaScript engines. However, Fuzzilli supports other engines as well (just check the Targets folder), including:
- JerryScript;
- QuickJS;
- Qt QJSEngine;
- XS; and
- duktape.
Your newly acquired knowledge and skills enable you to fuzz them on your own.
Good luck!
Useful links
- Coverage Guided Fuzzing for JavaScript Engines Thesis (PDF)
- OffensiveCon19 video
- OffensiveCon19 slides (PDF)
- How Fuzzilli Works
- Fuzzilli repository on GitHub