/RULF

A fully automated Rust fuzz driver generator

Primary LanguageRustOtherNOASSERTION

RULF: Fuzz Target Generator for Rust libraries

Introduction

This prototype tool is aimed at generating fuzz targets for Rust libraries automatically. A fuzz target is a function that accepts an array of bytes and exercises some library APIs. The targets generated by our tool is a set of shallow fuzz targets (the length of each target is mostly smaller than or equal to 3) to cover as many library APIs as possible. Each generated target can be compiled successfully. Then, you can fuzz these targets with afl.rs to find potential bugs.

The recommended workflow to use this tool to fuzz a library is as follows:

  1. clone the source and build this tool
  2. select a library for fuzzing, the use this tool to generate targets for the selected library.
  3. fuzz the library with afl.rs. We provide a command line script to partly automate the process.

Cite Our work

If you want to cite our work, you can cite our ASE'21 paper. The bibtex is as follows:

@article{jiang2021rulf,
  title={RULF: Rust library fuzzing via API dependency graph traversal},
  author={Jianfeng Jiang, Hui Xu, and Yangfan Zhou},
  journal={Prof. of the 36th IEEE/ACM International Conference on Automated Software Engineering (ASE)},
  year={2021}
}

How to use our tool with Docker

The recommend way to use and develop our tool is through docker. We prepare a dockerfile and several scripts to manage dependencies of RULF and afl.

The first three scripts are executed on host machine.

  1. Run docker/docker-build. This script will build an image containing the dependencies of RULF.
  2. Run scripts/enable-afl-on-host. This script will enable afl to run on your machine. Running this script requires logging as root. You can run sudo su to switch to root. Then exit to normal user.
  3. Run docker/docker-run. This script will start a docker container and map the current directory to the container. The following scripts are executed in the container.
  4. Run scripts/build-in-docker. This script will compile current project and set it as default toolchain with rustup. This scripts may fail several times due to network problem. Just retry it.
  5. Run scripts/install-and-test-afl. This script will download afl.rs and test whether afl can run on your machine. You should see the output window of afl to continue. Just type Ctrl+C to exit afl.
  6. Run scripts/install-fuzzing-scripts. This script will download our fuzzing scripts from github. It will also download source files of several test crates we use in our paper. Note: Sometimes downloading files from github may fail. You can download this project on host and copy it into the container(One example is docker/docker-cp). Then run this script again.

Then you can generate targets and fuzz them. For example, we want to fuzz url. You can run following commands.

afl_scripts -p url
afl_scripts -f 500 url
afl_scripts -b url
afl_scripts -fuzz url

More options can see documentation of our fuzzing scripts.

If you don't want to use our tool with docker. You can follow documentation below.

How to build this tool?

  1. building dependency: This repository is originated from the rust project. So, the dependency is the same as the dependency to build rust from source. You can refer to rust/RAEDME.md to install dependencies based on your operating system.

  2. build this tool: Suppose you are on a Unix-like system, and your work directory is $HOME, you can follow the below instuctions.

WORKDIR=$HOME #you can change this directory based on your own settings
cd $WORKDIR
git clone https://github.com/Artisan-Lab/RULF
cd $WORKDIR/RULF
python ./x.py build --stage 2 # Rustdoc will only be compiled in stage2, so a full stage2 compilation is required.

First time to build this tool may cost a long time (maybe 1 hour or more). For Rust supports incremental compilation, the time will be much shorter since the second time (if you made some modification to the source code).

After above instructions, You will get an executable fuzz-target-generator in the directory $WORKDIR/RULF/build/x86_64-unknown-linux-gnu/stage2/bin. You can add this directory to your environmental variable $PATH.

How to use this tool to generate targets for a given library?

I will take the crate url as an example to show how to use our tool.

Currently, we don't support pass command line options(for we borrow the options from rustdoc). So, to support a new crate, you need to add some code to the source directly and recompile the tool.

You can follow below instuctions:

  1. add the output directory of crate url in file $WORKDIR/RULF/src/librustdoc/fuzz_target/file_util.rs. For crate url, you can change the output directory of url in line 10 based on your own settings. Then recompile the tool. Suppose the path is URL_OUTPUT_PATH.

  2. download the source code of url. You can download source code from github or use cargo. To use cargo, you can add dependency url = "=2.2.0" in Cargo.toml of any Rust project and compile the project. cargo will download the project automatically. On my PC, the directory of the source is $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0. Use this directory to set environmental variable URL_SOURCE_PATH.

  3. generate fuzz targets for url. You can follow below instructions.

cd $URL_SOURCE_PATH
cargo clean
cargo doc -v

When you run cargo doc -v, you will see the details of commands to generate rustdoc. Then, find the one which generates doc for url.(usually, the last one). On my computer, the command is rustdoc --edition=2018 --crate-type lib --crate-name url src/lib.rs -o /home/jjf/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0/target/doc --error-format=json --json=diagnostic-rendered-ansi -L dependency=/home/jjf/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0/target/debug/deps --extern form_urlencoded=/home/jjf/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0/target/debug/deps/libform_urlencoded-322af90b85726206.rmeta --extern idna=/home/jjf/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0/target/debug/deps/libidna-9f0b442d9914b13a.rmeta --extern matches=/home/jjf/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0/target/debug/deps/libmatches-194969caaa695533.rmeta --extern percent_encoding=/home/jjf/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0/target/debug/deps/libpercent_encoding-1bc3c9463b6362c2.rmeta

Then, replace the first word rustdoc with fuzz-target-generator. So, the command on my PC is fuzz-target-generator --edition=2018 --crate-type lib --crate-name url src/lib.rs -o /home/jjf/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0/target/doc --error-format=json --json=diagnostic-rendered-ansi -L dependency=/home/jjf/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0/target/debug/deps --extern form_urlencoded=/home/jjf/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0/target/debug/deps/libform_urlencoded-322af90b85726206.rmeta --extern idna=/home/jjf/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0/target/debug/deps/libidna-9f0b442d9914b13a.rmeta --extern matches=/home/jjf/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0/target/debug/deps/libmatches-194969caaa695533.rmeta --extern percent_encoding=/home/jjf/.cargo/registry/src/github.com-1ecc6299db9ec823/url-2.2.0/target/debug/deps/libpercent_encoding-1bc3c9463b6362c2.rmeta.

Run this command and you will get fuzz targets in your $URL_OUTPUT_PATH.(We are trying to simplify the command with cargo.)

How to fuzz the generated targets with afl.rs and interpreting the fuzzing results?

Once you get fuzz targets, you can fuzz these targets with afl.rs. You can follow the instructions of afl.rs on your own. Or use our prepared command line scripts. More details can be seen in the README.md of our scripts.

Limitations

Currently, we don't support APIs with generics. Macros and async APIs are not supported too. APIs with parameters with static lifetime, e.g., &'static str, are also not supported.

Contributions

Any Contribution to this project is both under MIT and Apache License.