nim-eth/tests/fuzzing
kdeme 4b154e56c7
Fix aflLoop for non afl-clang-fast
2019-10-09 21:23:22 +02:00
..
discovery rename to fuzztest & change standalone define 2019-10-02 14:45:29 +03:00
rlp Add whisper encode decode test and rename rlp test 2019-10-02 14:45:29 +03:00
whisper Add whisper encode decode test and rename rlp test 2019-10-02 14:45:29 +03:00
config.nims Add basic fuzzing build tasks config.nims file 2019-10-02 14:45:29 +03:00
fuzz.nims split functionality and script 2019-10-02 14:45:29 +03:00
fuzz_helpers.nim Add aflInit and aflLoop + add comments 2019-10-08 16:23:57 +02:00
fuzztest.nim Fix aflLoop for non afl-clang-fast 2019-10-09 21:23:22 +02:00
readme.md Add readme file 2019-10-08 16:57:31 +02:00

readme.md

Fuzzing

tldr:

  • Install afl.
  • Create a testcase.
  • Run: nim fuzz.nims afl testfolder/testcase.nim

Or

  • Install libFuzzer (comes with LLVM).
  • Create a testcase.
  • Run: nim fuzz.nims libFuzzer testfolder/testcase.nim

Fuzzing Helpers

There are two convenience templates which will help you set up a quick fuzzing test.

These are the mandatory test block and the optional init block.

Example usage:

test:
  var rlp = rlpFromBytes(@payload.toRange)
  discard rlp.inspect()

Any unhandled Exception will result in a failure of the testcase. If certain Exceptions are to be allowed to occur within the test, they should be caught.

E.g.:

test:
  try:
    var rlp = rlpFromBytes(@payload.toRange)
    discard rlp.inspect()
  except RlpError:
    debug "Inspect failed", err = getCurrentExceptionMsg()

Supported Fuzzers

The two templates can prepare the code for both afl and libFuzzer.

You will need to install first the fuzzer you want to use.

Install afl

# Ubuntu / Debian
sudo apt-get install afl

# Fedora
dnf install american-fuzzy-lop
# for usage with clang & clang-fast you will have to install
# american-fuzzy-lop-clang or american-fuzzy-lop-clang-fast

# Arch Linux
pacman -S afl

# NixOS
nix-env -i afl

Install libFuzzer

LibFuzzer is part of llvm and will be installed together with llvm-libs in recent versions. Installing clang should install llvm-libs.

# Ubuntu / Debian
sudo apt-get install clang

# Fedora
dnf install clang

# Arch Linux
pacman -S clang

# NixOS
nix-env -iA nixos.clang_7 nixos.llvm_7

Compiling & Starting the Fuzzer

Scripted helper

There is a nimscript helper to compile & start the fuzzer:

# for afl
nim fuzz.nims afl testcase.nim

# for libFuzzer
nim fuzz.nims libFuzzer testcase.nim

Manually with afl

Compiling

With gcc:

nim c -d:standalone -d:release -d:chronicles_log_level=fatal -d:noSignalHandler --cc=gcc --gcc.exe=afl-gcc --gcc.linkerexe=afl-gcc testcase.nim

The standalone define is specifically required for the init and test templates.

You typically want to fuzz in -d:release and probably also want to lower down the logging. But this is not strictly necessary.

There is also a nimscript task in config.nims for this:

nim c build_afl testcase.nim

With clang:

# afl-clang
nim c -d:standalone -d:noSignalHandler --cc=clang --clang.exe=afl-clang --clang.linkerexe=afl-clang ftestcase.nim
# afl-clang-fast
nim c -d:standalone -d:noSignalHandler --cc=clang --clang.exe=afl-clang-fast --clang.linkerexe=afl-clang-fast testcase.nim

Starting the Fuzzer

To start the fuzzer:

afl-fuzz -i input -o results -- ./testcase

To rerun it without losing previous results/corpus:

afl-fuzz -i - -o results -- ./testcase

To run several parallel fuzzing sessions:

# Start master fuzzer
afl-fuzz -i input -o results -M fuzzer01 -- ./testcase
# Start slaves (usually 1 per core available)
afl-fuzz -i input -o results -S fuzzer02 -- ./testcase
afl-fuzz -i input -o results -S fuzzer03 -- ./testcase
# add more if needed

When compiled with -d:standalone the resulting application can also be run manually by providing it input data, e.g.:

cat testfile | ./testcase

Manually with libFuzzer

Compiling

nim c -d:release -d:chronicles_log_level=fatal --noMain --cc=clang --passC="-fsanitize=fuzzer" --passL="-fsanitize=fuzzer" testcase.nim

You typically want to fuzz in -d:release and probably also want to lower down the logging. But this is not strictly necessary.

There is also a nimscript task in config.nims for compiling:

nim c build_libFuzzer testcase.nim

Starting the Fuzzer

Starting the fuzzer is as simple as running the compiled program:

./testcase corpus_dir -runs=1000000

To see the available options:

./testcase test=1

Parallel fuzzing on 8 cores:

./fuzz-libfuzzer -jobs=8 -workers=8

You can also use the application to verify a specific test case:

./testcase input_file

Additional notes

The init template, when used with afl, is only cosmetic. It will be run before each test block, compared to libFuzzer, where it will be run only once.

In case of using afl with alf-clang-fast you can make use of aflInit() and aflLoop() calls.

aflInit() will allow using what is called deferred instrumentation. Basically, the forking of the process will only happen after this call, where normally it is done right before main().

aflLoop(cint) will allow for (experimental) persistant mode. This is more comparable with libFuzzer.

These calls are enabled with -d:clangfast.