Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove libfuzzer support from the fuzzer #1611

Merged
merged 6 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fuzz-tests/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ fuzz-*.log
#coverage
*.raw
corpus_*.lst
corpus_*/
2 changes: 0 additions & 2 deletions fuzz-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ edition = "2021"
cargo-fuzz = true

[dependencies]
libfuzzer-sys = { version = "0.4", optional = true }
afl = { version = "0.14.1", optional = true }
clap = { version = "4.1.7", optional = true }
rand = { version = "0.8.5", optional = true }
Expand Down Expand Up @@ -90,7 +89,6 @@ dummy_fuzzing = []

verbose = []

#fuzzer = [ "libfuzzer-sys" ]
#fuzzer = [ "afl" ]
fuzzer = ["simple-fuzzer"]

Expand Down
83 changes: 26 additions & 57 deletions fuzz-tests/README.md
Original file line number Diff line number Diff line change
@@ -1,64 +1,40 @@
# Introduction

This package implements fuzz tests for Radix Engine.
Currently it uses 3 fuzzing engines:
* [LibFuzzer](https://llvm.org/docs/LibFuzzer.html)
It is wrapped by crate: [cargo-fuzz](https://docs.rs/crate/cargo-fuzz/0.11.2)
Currently it uses 2 fuzzing engines:
* [AFLplusplus](https://aflplus.plus/)
It is wrapped by crate: [cargo-afl](https://docs.rs/afl/0.12.14/afl)
It is wrapped by crate: [cargo-afl](https://docs.rs/afl/0.14.1/afl)
* simple-fuzzer
This is a very simple fuzzer, which is especially useful when developing new fuzz tests.
It allows to:
- quickly rebuild a test and perform some simple checks (building libfuzzer or AFL takes ages).
- quickly rebuild a test and perform some simple checks (building AFL takes ages).
- reproduce a crash (or other problematic case) using provided input file,
(which might be generated by libfuzzer or AFL)
(which might be generated by AFL)

# LibFuzzer
* At the moment it is possible to use it only with rust `nightly` builds.
* Must be built with `no-cfg-fuzzing` option, to skip setting `fuzzing` flag.
Crate `secp256k1` uses some stubs instead of true cryptography if `fuzzing` is set
(for details see: https://github.com/rust-bitcoin/rust-secp256k1/#fuzzing), which makes it unusable
for our purposes.
* It does not require fuzz input data, but it is preferred to provide it to increase fuzzing efficiency.
# AFL
* It sets `fuzzing` compilation config by default.
Be aware that some crates might change behaviour depending on this flag.
See [secp256k1](https://github.com/rust-bitcoin/rust-secp256k1/#fuzzing), which was using
this flag before switching to `secp256k1_fuzz`.

## Installation
```
cargo +nightly install cargo-fuzz
```
However, at the moment it looks like it is safe to use `fuzzing` config within Radix Engine and
its dependencies.

# AFL
* Similarly to `libfuzzer` it sets `fuzzing` flag by default.
Thus `cargo afl` shall be built and installed using below command
```
cargo install afl --features no_cfg_fuzzing
```
* It requires some input data to be provided.

## Installation
One can use convenience script [install_afl.sh](./install_afl.sh) to install `cargo-afl` with the change mentioned above.

## Machine setup
Before starting to fuzz AFL checks machine configuration and requests to adjust some settings if required.
* Linux
- do not send core dump notifications to external utilities.
This is to notify fuzzer immediately about the crash, so it does not misinterpret crash as timeout.
`sudo bash -c "echo core > /proc/sys/kernel/core_pattern"`

Alternatively it is possible to use env `AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1`
- disable CPU frequency scaling to provide best performance
`sudo bash -c "cd /sys/devices/system/cpu ; echo performance | tee cpu*/cpufreq/scaling_governor >/dev/null"`

Alternatively it is possible to use env `AFL_SKIP_CPUFREQ=1`

* MacOS
If you see an error message like `shmget() failed` above, try running the following command:
```
sudo /Users/<username>/.local/share/afl.rs/rustc-1.67.1-d5a82bb/afl.rs-0.12.14/afl/bin/afl-system-config
```
To adjust settings for best performance following command shall be called:
```
cargo afl system-config
```

# Input data
Sample input data shall be placed in `fuzz_input` folder in dedicated subfolder.
`libfuzzer` and `AFL` are able to use the data in the same format.
`AFL` is able to use the data in the same format.

In order to minimize the test corpus, the data might be prior processed.
Processing method is specific for fuzzing engine.
Expand All @@ -67,9 +43,11 @@ One can use following command to generate input data (`cargo afl cmin|tmin` are
`./fuzz.sh generate-input [raw|unique|minimize]`
Check `./fuzz.sh help` for more details

# Fuzz tests
Available fuzz tests:
* transaction - Fuzzes transaction manifests and tries to execute it
# Fuzz targets
Available fuzz targets might be listed with following command:
```
./list-fuzz-targets.sh
```

# Usage

Expand Down Expand Up @@ -106,28 +84,19 @@ or
```
cargo afl whatsup afl/transaction
```
* LibFuzzer
- Run 2 LibFuzzer sessions and do not stop if crash discovered
```
cargo +nightly fuzz run --release --no-default-features --features std,libfuzzer-sys --fuzz-dir . --no-cfg-fuzzing --target-dir target-libfuzzer transaction -- -create_missing_dirs=1 -jobs=2 -fork=1 -ignore_crashes=1
```
If fuzzer is running in forked mode it logs to file (file per job) `file-<job_number>.log`.
One can monitor logs using below command:
- reproduce some crash discovered by `afl` using `afl`
```
tail -f file-*.log
```
- Reproduce discovered crash
```
cargo +nightly fuzz run --release --no-default-features --features std,libfuzzer-sys --fuzz-dir . --no-cfg-fuzzing --target-dir target-libfuzzer transaction artifacts/transaction/crash-0734fdddb6de62d6d954c06c65b310d185656e10
cat afl/transaction/0_fast/queue/id:000001,time:0,execs:0,orig:system_001.raw | ./target-afl/release/transaction
```

* simple-fuzzer
- Reproduce discovered crash
```
./fuzz.sh simple run artifacts/transaction/crash-0734fdddb6de62d6d954c06c65b310d185656e10
./fuzz.sh simple run afl/transaction/0_fast/crashes/id:000168,sig:06,src:001128+000312,time:260091,execs:21509,op:splice,rep:8
```
or
```
RUST_BACKTRACE=1 cargo run --release --no-default-features --features std,simple-fuzzer --bin transaction -- -v artifacts/transaction/crash-0734fdddb6de62d6d954c06c65b310d185656e10
RUST_BACKTRACE=1 ./fuzz.sh simple run transaction afl/transaction/0_fast/crashes/id:000168,sig:06,src:001128+000312,time:260091,execs:21509,op:splice,rep:8
```

# Future considerations
Expand Down
80 changes: 8 additions & 72 deletions fuzz-tests/fuzz.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,9 @@ function usage() {
echo " $t"
done
echo "Available fuzzers"
echo " libfuzzer - 'cargo fuzz' wrapper"
echo " afl - 'cargo afl' wrapper"
echo " simple - simple fuzzer (default)"
echo " Subcommands:"
echo " init - Take sample input ('./fuzz_input/<target>') for given test,"
echo " minimize the test corpus and put the result into 'corpus/<target>',"
echo " which is used by 'libfuzzer' as initial input."
echo " Applicable only for 'libfuzzer'."
echo " build - Build fuzz test for given fuzzer."
echo " Binaries are built in 'release' format."
echo " run - Run fuzz test for given fuzzer (default command)"
Expand Down Expand Up @@ -58,16 +53,14 @@ function usage() {
echo " $0 afl build transaction"
echo " - run AFL tests for 1h"
echo " $0 afl run transaction -V 3600"
echo " - run LibFuzzer for 1h"
echo " $0 libfuzzer run transaction -max_total_time=3600"
echo " - run simple-fuzzer for 1h"
echo " $0 simple run transaction --duration 3600"
echo " - run simple-fuzzer in 'release' mode for 1h"
echo " $0 simple run --release transaction --duration 3600"
echo " - reproduce some crash discovered by 'libfuzzer'"
echo " $0 libfuzzer run transaction ./artifacts/transaction/crash-ec25d9d2a8c3d401d84da65fd2321cda289d"
echo " - reproduce some crash discovered by 'libfuzzer' using 'simple-fuzzer'"
echo " RUST_BACKTRACE=1 $0 simple run transaction ./artifacts/transaction/crash-ec25d9d2a8c3d401d84da65fd2321cda289d"
echo " - reproduce some crash discovered by 'afl' using 'simple-fuzzer'"
echo " RUST_BACKTRACE=1 $0 simple run transaction afl/transaction/0_fast/crashes/id:000168,sig:06,src:001128+000312,time:260091,execs:21509,op:splice,rep:8"
echo " - reproduce some crash discovered by 'afl' using 'afl'"
echo " cat afl/transaction/0_fast/queue/id:000001,time:0,execs:0,orig:system_001.raw | ./target-afl/release/transaction"

exit 1
}
Expand All @@ -89,48 +82,6 @@ function check_target_available() {
return 1
}

function fuzzer_libfuzzer() {
local cmd=$DFLT_SUBCOMMAND
if [ $# -ge 1 ] ; then
cmd=$1
shift
fi
local target=$DFLT_TARGET
if [ $# -ge 1 ] ; then
target=$1
shift
fi
local run_args=""
if [ "$cmd" = "run" ] ; then
run_args="$@"

elif [ "$cmd" = "init" ] ; then
# initial setup:
# - minimize the corpus:
# https://llvm.org/docs/LibFuzzer.html#id25
#
# cargo +nightly fuzz $target --fuzz-dir radix-engine-fuzz \
# --no-cfg-fuzzing --target-dir target-libfuzzer $target -- \
# -merge=1 corpus/$target <INTERESTING_INPUTS_DIR/FULL_CORPUS_DIR>
#
cmd=run
run_args="-- -merge=1 corpus/${target} fuzz_input/${target} "
fi
# Unset cfg=fuzzing by --no-cfg-fuzzing.
# "secp256k1" uses some stubs instead of true cryptography if "fuzzing" is set.
# see: https://github.com/rust-bitcoin/rust-secp256k1/#fuzzing
set -x
cargo +nightly fuzz $cmd \
--release \
--no-default-features --features std,libfuzzer-sys\
--fuzz-dir . \
--no-cfg-fuzzing \
--target-dir target-libfuzzer \
$target \
$run_args

}

function fuzzer_afl() {
local cmd=$DFLT_SUBCOMMAND
if [ $# -ge 1 ] ; then
Expand All @@ -155,20 +106,7 @@ function fuzzer_afl() {
set -x
cargo afl fuzz -i fuzz_input/${target} -o afl/${target} $@ -- target-afl/release/${target}
elif [ $cmd = "machine-init" ] ; then
uname="$(uname -s)"
if [ $uname = "Linux" ] ; then
set -x
# disable external utilities handling coredumps
sudo bash -c "echo core > /proc/sys/kernel/core_pattern"
# disable CPU frequency scaling
find /sys/devices/system/cpu -name scaling_governor | \
xargs -I {} sudo bash -c "echo performance > {}"
elif [ $uname = "Darwin" ] ; then
echo "If you see an error message like 'shmget() failed' above, try running the following command:"
echo " sudo /Users/<username>/.local/share/afl.rs/rustc-<version>/afl.rs-<version>/afl/bin/afl-system-config"
else
error "OS '$uname' not supported"
fi
cargo afl system-config
fi
}

Expand Down Expand Up @@ -240,7 +178,7 @@ function generate_input() {

if [ $target != "wasm_instrument" ] ; then
# Collect input data
cargo nextest run test_${target}_generate_fuzz_input_data --release
cargo nextest run --no-default-features --features std,simple-fuzzer test_${target}_generate_fuzz_input_data --release

if [ $mode = "raw" ] ; then
#mv ../radix-engine-tests/manifest_*.raw ${curr_path}/${final_dir}
Expand Down Expand Up @@ -297,7 +235,7 @@ function generate_input() {
}

if [ $# -ge 1 ] ; then
# available fuzzers/commands: libfuzzer, afl, simple, generate-input
# available fuzzers/commands: afl, simple, generate-input
cmd=$1
shift
else
Expand All @@ -308,9 +246,7 @@ if [ $# -eq 0 ] ; then
usage
fi

if [ $cmd = "libfuzzer" ] ; then
fuzzer_libfuzzer $@
elif [ $cmd = "afl" ] ; then
if [ $cmd = "afl" ] ; then
fuzzer_afl $@
elif [ $cmd = "simple" ] ; then
fuzzer_simple $@
Expand Down
12 changes: 1 addition & 11 deletions fuzz-tests/get-coverage-data.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,6 @@ if [ $# -ge 1 ] ; then
shift
fi

get_libfuzzer_corpus_files() {
local target=$1
local corpus_dir="corpus/${target}"

if [ -d $corpus_dir ] ; then
find $corpus_dir -type f
fi
}

get_afl_corpus_files() {
local target=$1
local corpus_dir="afl/${target}"
Expand Down Expand Up @@ -87,7 +78,7 @@ minimize_corpus() {

tmp_list=$(mktemp)
find $cmin_dir -type f > $tmp_list
cp $tmp_list $list
mv $tmp_list $list
}

process_corpus_files() {
Expand Down Expand Up @@ -122,7 +113,6 @@ for t in $targets ; do
list=corpus_files_$t.lst
rm -f $list
get_afl_corpus_files $t > $list
get_libfuzzer_corpus_files $t >> $list

if [ -s $list ] ; then
if [ "$corpus_mode" = "cmin" ] ; then
Expand Down
2 changes: 0 additions & 2 deletions fuzz-tests/src/bin/attached_modules/role_assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
//! ensure that if they've resulted in an error, it's not a native vm trap.
//! 7. No roles may be set after the creation of the module.

#![cfg_attr(feature = "libfuzzer-sys", no_main)]

use arbitrary::Arbitrary;

use fuzz_tests::continue_if_manifest_is_unpreparable;
Expand Down
1 change: 0 additions & 1 deletion fuzz-tests/src/bin/decimal.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#![cfg_attr(feature = "libfuzzer-sys", no_main)]
use arbitrary::Arbitrary;
use fuzz_tests::fuzz_template;
use radix_engine_common::math::*;
Expand Down
1 change: 0 additions & 1 deletion fuzz-tests/src/bin/parse_decimal.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#![cfg_attr(feature = "libfuzzer-sys", no_main)]
use fuzz_tests::fuzz_template;
use radix_engine_common::math::Decimal;

Expand Down
2 changes: 0 additions & 2 deletions fuzz-tests/src/bin/system.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#![cfg_attr(feature = "libfuzzer-sys", no_main)]

use arbitrary::Arbitrary;
use fuzz_tests::fuzz_template;
use native_sdk::modules::metadata::Metadata;
Expand Down
20 changes: 0 additions & 20 deletions fuzz-tests/src/bin/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
#![cfg_attr(feature = "libfuzzer-sys", no_main)]

#[cfg(feature = "libfuzzer-sys")]
use libfuzzer_sys::fuzz_target;
#[cfg(feature = "libfuzzer-sys")]
use once_cell::sync::Lazy;

#[cfg(feature = "afl")]
use afl::fuzz;
#[cfg(feature = "afl")]
Expand All @@ -16,19 +9,6 @@ use fuzz_tests::fuzz;
use fuzz_tests::transaction::fuzz_tx::*;

// Fuzzer entry points
#[cfg(feature = "libfuzzer-sys")]
fuzz_target!(|data: &[u8]| {
unsafe {
static mut FUZZER: Lazy<TxFuzzer> = Lazy::new(|| {
fuzz_tx_init_statics();
TxFuzzer::new()
});

FUZZER.reset_runner();
FUZZER.fuzz_tx_manifest(data);
}
});

#[cfg(feature = "afl")]
fn main() {
fuzz_tx_init_statics();
Expand Down
1 change: 0 additions & 1 deletion fuzz-tests/src/bin/wasm_instrument.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#![cfg_attr(feature = "libfuzzer-sys", no_main)]
use fuzz_tests::fuzz_template;
use radix_engine::vm::wasm::{PrepareError, WasmModule, WasmValidatorConfigV1};

Expand Down
Loading
Loading