Skip to content

Commit

Permalink
Merge #2252: Fuzzing framework support
Browse files Browse the repository at this point in the history
d059544 [Build] fuzz target, change LIBBITCOIN_ZEROCOIN link order. (furszy)
2396e6b [fuzz] Add ContextualCheckTransaction call to transaction target. (furszy)
f0887a0 Fuzzing documentation "PIVX-fication" (furszy)
9631f46 [doc] add sanitizers documentation in developer-notes.md (furszy)
70a0ace tests: Test serialisation as part of deserialisation fuzzing. Test round-trip equality where possible. Avoid code repetition. (practicalswift)
e1b92b6 ignore new fuzz targets gitignore (furszy)
d058d8c tests: Add deserialization fuzzing harnesses (furszy)
e1f666c tests: Remove TRANSACTION_DESERIALIZE (replaced by transaction fuzzer) (practicalswift)
b5f291c tests: Add fuzzing harness for CheckTransaction(...), IsStandardTx(...) and other CTransaction related functions (furszy)
3205871 fuzz: Remove option --export_coverage from test_runner (MarcoFalke)
52693ee fuzz: Add option to merge input dir to test runner (MarcoFalke)
2b4f8aa doc: Remove --disable-ccache from docs (MarcoFalke)
b54b1d6 tests: Improve test runner output in case of target errors (practicalswift)
cd6134f test: Log output even if fuzzer failed (MarcoFalke)
48cd0c8 doc: Improve fuzzing docs for macOS users (Fabian Jahr)
d642b67 [Build] Do not disable wallet when fuzz is enabled. (furszy)
c3447b5 Update doc and CI config (qmma)
1266d3e Disable other targets when enable-fuzz is set (qmma)
f28ac9a build: Allow to configure --with-sanitizers=fuzzer (MarcoFalke)
425742c fuzz: test_runner: Better error message when built with afl (MarcoFalke)
541f442 qa: Add test/fuzz/test_runner.py (MarcoFalke)
89fe5b2 Add missing LIBBITCOIN_ZMQ to test target (furszy)
58dbe79 add fuzzing binaries to gitignore. (furszy)
393a126 fuzz: Move deserialize tests to test/fuzz/deserialize.cpp (MarcoFalke)
a568df5 test: Build fuzz targets into separate executables (furszy)
d5dddde [test] fuzz: make test_one_input return void (MarcoFalke)
2e4ec58 [fuzzing] initialize chain params by default. (furszy)
08d8ebe [tests] Add libFuzzer support. (practicalswift)
84f72da [test] Speed up fuzzing by ~200x when using afl-fuzz (practicalswift)
faf2be6 Init ECC context for test_bitcoin_fuzzy. (Gregory Maxwell)
11150df Make fuzzer actually test CTxOutCompressor (Pieter Wuille)
d6f6a85 doc: Add bare-bones documentation for fuzzing (Wladimir J. van der Laan)
5c3b550 Simple fuzzing framework (pstratem)

Pull request description:

  As the title says, adding fuzzing framework support so we can start getting serious on this area as well.

  Adapted the following PRs:

  * bitcoin#9172.
  * bitcoin#9354.
  * bitcoin#9691.
  * bitcoin#10415.
  * bitcoin#10440.
  * bitcoin#15043.
  * bitcoin#15047.
  * bitcoin#15295.
  * bitcoin#15399 (fabcfa5 only).
  * bitcoin#16338.
  * bitcoin#17051.
  * bitcoin#17076.
  * bitcoin#17225.
  * bitcoin#17942.
  * bitcoin#16236 (only fa35c42).
  * bitcoin#18166 (only f2472f6).
  * bitcoin#18300.
  * And.. probably will go further and continue adapting more PRs..

ACKs for top commit:
  random-zebra:
    utACK d059544 and merging...

Tree-SHA512: c0b05bca47bf99bafd8abf1453c5636fe05df75f16d0e9c750368ea2aed8142f0b28d28af1d23468b8829188412a80fd3b7bdbbda294b940d78aec80c1c7d03a
  • Loading branch information
random-zebra committed May 28, 2021
2 parents 2f0d2d0 + d059544 commit 44b5327
Show file tree
Hide file tree
Showing 15 changed files with 1,041 additions and 10 deletions.
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,29 @@ src/pivx-cli
src/pivx-tx
src/bench/bench_pivx
src/test/test_pivx
src/test/test_pivx_fuzzy
src/qt/test/test_pivx-qt
src/test/fuzz/address_deserialize
src/test/fuzz/addrman_deserialize
src/test/fuzz/banentry_deserialize
src/test/fuzz/block_deserialize
src/test/fuzz/blockheader_deserialize
src/test/fuzz/blocklocator_deserialize
src/test/fuzz/blockmerkleroot
src/test/fuzz/blockundo_deserialize
src/test/fuzz/bloomfilter_deserialize
src/test/fuzz/coins_deserialize
src/test/fuzz/diskblockindex_deserialize
src/test/fuzz/inv_deserialize
src/test/fuzz/messageheader_deserialize
src/test/fuzz/netaddr_deserialize
src/test/fuzz/service_deserialize
src/test/fuzz/transaction_deserialize
src/test/fuzz/txoutcompressor_deserialize
src/test/fuzz/txundo_deserialize
src/test/fuzz/transaction
src/test/fuzz/*deserialize


# autoreconf
Makefile.in
Expand Down
5 changes: 5 additions & 0 deletions .travis/test_04_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
export LC_ALL=C.UTF-8

travis_retry docker pull "$DOCKER_NAME_TAG"

export DIR_FUZZ_IN=${TRAVIS_BUILD_DIR}/qa-assets
git clone https://github.com/bitcoin-core/qa-assets ${DIR_FUZZ_IN}
export DIR_FUZZ_IN=${DIR_FUZZ_IN}/fuzz_seed_corpus/

env | grep -E '^(BITCOIN_CONFIG|CCACHE_|WINEDEBUG|LC_ALL|BOOST_TEST_RANDOM|CONFIG_SHELL)' | tee /tmp/env
if [[ $HOST = *-mingw32 ]]; then
DOCKER_ADMIN="--cap-add SYS_ADMIN"
Expand Down
6 changes: 6 additions & 0 deletions .travis/test_06_script_b.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,10 @@ if [ "$RUN_FUNCTIONAL_TESTS" = "true" ]; then
END_FOLD
fi

if [ "$RUN_FUZZ_TESTS" = "true" ]; then
BEGIN_FOLD fuzz-tests
DOCKER_EXEC test/fuzz/test_runner.py -l DEBUG ${DIR_FUZZ_IN}
END_FOLD
fi

cd ${TRAVIS_BUILD_DIR} || (echo "could not enter travis build dir $TRAVIS_BUILD_DIR"; exit 1)
3 changes: 2 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ dist_noinst_SCRIPTS = autogen.sh
EXTRA_DIST = $(DIST_SHARE) $(DIST_CONTRIB) $(DIST_DOCS) $(DIST_CARGO) $(WINDOWS_PACKAGING) $(LINUX_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS)

EXTRA_DIST += \
test/functional
test/functional \
test/fuzz

EXTRA_DIST += \
test/util/bitcoin-util-test.py \
Expand Down
47 changes: 40 additions & 7 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ AC_ARG_WITH([params-dir],
],
[params_path=""])

# Enable wallet
AC_ARG_ENABLE([wallet],
[AS_HELP_STRING([--disable-wallet],
[disable wallet (enabled by default)])],
Expand Down Expand Up @@ -167,6 +166,12 @@ AC_ARG_ENABLE([extended-functional-tests],
[use_extended_functional_tests=$enableval],
[use_extended_functional_tests=no])

AC_ARG_ENABLE([fuzz],
AS_HELP_STRING([--enable-fuzz],
[enable building of fuzz targets (default no). enabling this will disable all other targets]),
[enable_fuzz=$enableval],
[enable_fuzz=no])

AC_ARG_WITH([qtcharts],
[AS_HELP_STRING([--with-qtcharts],
[enable qtcharts support (default is yes if qt is enabled and qtchartview is found)])],
Expand Down Expand Up @@ -314,7 +319,14 @@ if test x$use_sanitizers != x; then
AX_CHECK_LINK_FLAG(
[[-fsanitize=$use_sanitizers]],
[[SANITIZER_LDFLAGS=-fsanitize=$use_sanitizers]],
[AC_MSG_ERROR([linker did not accept requested flags, you are missing required libraries])])
[AC_MSG_ERROR([linker did not accept requested flags, you are missing required libraries])],
[],
[AC_LANG_PROGRAM([[
#include <cstdint>
#include <cstddef>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { return 0; }
__attribute__((weak)) // allow for libFuzzer linking
]],[[]])])
fi

ERROR_CXXFLAGS=
Expand Down Expand Up @@ -996,6 +1008,28 @@ AC_SUBST(LEVELDB_CPPFLAGS)
AC_SUBST(LIBLEVELDB)
AC_SUBST(LIBMEMENV)

dnl enable-fuzz should disable all other targets
if test "x$enable_fuzz" = "xyes"; then
AC_MSG_WARN(enable-fuzz will disable other targets)
build_bitcoin_utils=no
build_bitcoin_cli=no
build_bitcoin_tx=no
build_bitcoind=no
build_bitcoin_libs=no
bitcoin_enable_qt=no
bitcoin_enable_qt_test=no
bitcoin_enable_qt_dbus=no
enable_wallet=yes # needs to be built for now.
use_bench=no
use_upnp=no
use_zmq=no
else
BITCOIN_QT_INIT

dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus
BITCOIN_QT_CONFIGURE([$use_pkgconfig])
fi

if test x$enable_wallet != xno; then
dnl Check for libdb_cxx only if wallet enabled
BITCOIN_FIND_BDB48
Expand Down Expand Up @@ -1030,11 +1064,6 @@ if test x$have_miniupnpc != xno; then
fi
fi

BITCOIN_QT_INIT

dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus
BITCOIN_QT_CONFIGURE([$use_pkgconfig])

if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononono; then
use_boost=no
else
Expand Down Expand Up @@ -1409,6 +1438,7 @@ AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows])
AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes])
AM_CONDITIONAL([ENABLE_MINING_RPC],[test x$enable_mining_rpc = xyes])
AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes])
AM_CONDITIONAL([ENABLE_FUZZ],[test x$enable_fuzz = xyes])
AM_CONDITIONAL([ENABLE_QT],[test x$bitcoin_enable_qt = xyes])
AM_CONDITIONAL([ENABLE_QT_TESTS],[test x$BUILD_TEST_QT = xyes])
AM_CONDITIONAL([ENABLE_BENCH],[test x$use_bench = xyes])
Expand Down Expand Up @@ -1556,6 +1586,9 @@ if test x$bitcoin_enable_qt != xno; then
fi
echo " with zmq = $use_zmq"
echo " with test = $use_tests"
if test x$use_tests != xno; then
echo " with fuzz = $enable_fuzz"
fi
echo " with bench = $use_bench"
echo " with upnp = $use_upnp"
echo " with params = $params_path"
Expand Down
1 change: 1 addition & 0 deletions doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ The PIVX repo's [root README](/README.md) contains relevant information on the d
### Miscellaneous
- [Assets Attribution](assets-attribution.md)
- [Files](files.md)
- [Fuzz-testing](fuzzing.md)
- [Reduce Memory](reduce-memory.md)
- [Tor Support](tor.md)
- [Init Scripts (systemd/upstart/openrc)](init.md)
Expand Down
42 changes: 42 additions & 0 deletions doc/developer-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,48 @@ make cov
# A coverage report will now be accessible at `./test_pivx.coverage/index.html`.
```

**Sanitizers**
PIVX can be compiled with various "sanitizers" enabled, which add
instrumentation for issues regarding things like memory safety, thread race
conditions, or undefined behavior. This is controlled with the
`--with-sanitizers` configure flag, which should be a comma separated list of
sanitizers to enable. The sanitizer list should correspond to supported
`-fsanitize=` options in your compiler. These sanitizers have runtime overhead,
so they are most useful when testing changes or producing debugging builds.
Some examples:
```bash
# Enable both the address sanitizer and the undefined behavior sanitizer
./configure --with-sanitizers=address,undefined
# Enable the thread sanitizer
./configure --with-sanitizers=thread
```
If you are compiling with GCC you will typically need to install corresponding
"san" libraries to actually compile with these flags, e.g. libasan for the
address sanitizer, libtsan for the thread sanitizer, and libubsan for the
undefined sanitizer. If you are missing required libraries, the configure script
will fail with a linker error when testing the sanitizer flags.
The test suite should pass cleanly with the `thread` and `undefined` sanitizers,
but there are a number of known problems when using the `address` sanitizer. The
address sanitizer is known to fail in
[sha256_sse4::Transform](/src/crypto/sha256_sse4.cpp) which makes it unusable
unless you also use `--disable-asm` when running configure. We would like to fix
sanitizer issues, so please send pull requests if you can fix any errors found
by the address sanitizer (or any other sanitizer).
Not all sanitizer options can be enabled at the same time, e.g. trying to build
with `--with-sanitizers=address,thread` will fail in the configure script as
these sanitizers are mutually incompatible. Refer to your compiler manual to
learn more about these options and which sanitizers are supported by your
compiler.
Additional resources:
* [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html)
* [LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html)
* [MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html)
* [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html)
* [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)
* [GCC Instrumentation Options](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html)
* [Google Sanitizers Wiki](https://github.com/google/sanitizers/wiki)
* [Issue #12691: Enable -fsanitize flags in Travis](https://github.com/bitcoin/bitcoin/issues/12691)

Locking/mutex usage notes
-------------------------

Expand Down
138 changes: 138 additions & 0 deletions doc/fuzzing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
Fuzz-testing PIVX Core
==========================

A special test harness in `src/test/fuzz/` is provided for each fuzz target to
provide an easy entry point for fuzzers and the like. In this document we'll
describe how to use it with AFL and libFuzzer.

## Preparing fuzzing

The fuzzer needs some inputs to work on, but the inputs or seeds can be used
interchangeably between libFuzzer and AFL.

Extract the example seeds (or other starting inputs) into the inputs
directory before starting fuzzing.

```
git clone https://github.com/bitcoin-core/qa-assets
export DIR_FUZZ_IN=$PWD/qa-assets/fuzz_seed_corpus
```

AFL needs an input directory with examples, and an output directory where it
will place examples that it found. These can be anywhere in the file system,
we'll define environment variables to make it easy to reference them.

So, only for AFL you need to configure the outputs path:

```
mkdir outputs
export AFLOUT=$PWD/outputs
```

libFuzzer will use the input directory as output directory.

## AFL

### Building AFL

It is recommended to always use the latest version of afl:
```
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
tar -zxvf afl-latest.tgz
cd afl-<version>
make
export AFLPATH=$PWD
```

For macOS you may need to ignore x86 compilation checks when running `make`:
`AFL_NO_X86=1 make`.

### Instrumentation

To build PIVX Core using AFL instrumentation (this assumes that the
`AFLPATH` was set as above):
```
./configure --disable-shared --enable-tests --enable-fuzz CC=${AFLPATH}/afl-gcc CXX=${AFLPATH}/afl-g++
export AFL_HARDEN=1
make
```

If you are using clang you will need to substitute `afl-gcc` with `afl-clang`
and `afl-g++` with `afl-clang++`, so the first line above becomes:
```
./configure --disable-shared --enable-tests --enable-fuzz CC=${AFLPATH}/afl-clang CXX=${AFLPATH}/afl-clang++
```

We disable ccache because we don't want to pollute the ccache with instrumented
objects, and similarly don't want to use non-instrumented cached objects linked
in.

The fuzzing can be sped up significantly (~200x) by using `afl-clang-fast` and
`afl-clang-fast++` in place of `afl-gcc` and `afl-g++` when compiling. When
compiling using `afl-clang-fast`/`afl-clang-fast++` the resulting
binary will be instrumented in such a way that the AFL
features "persistent mode" and "deferred forkserver" can be used. See
https://github.com/google/AFL/tree/master/llvm_mode for details.

### Fuzzing

To start the actual fuzzing use:

```
export FUZZ_TARGET=bech32 # Pick a fuzz_target
mkdir ${AFLOUT}/${FUZZ_TARGET}
$AFLPATH/afl-fuzz -i ${DIR_FUZZ_IN}/${FUZZ_TARGET} -o ${AFLOUT}/${FUZZ_TARGET} -m52 -- src/test/fuzz/${FUZZ_TARGET}
```

You may have to change a few kernel parameters to test optimally - `afl-fuzz`
will print an error and suggestion if so.

On macOS you may need to set `AFL_NO_FORKSRV=1` to get the target to run.
```
export FUZZ_TARGET=bech32 # Pick a fuzz_target
mkdir ${AFLOUT}/${FUZZ_TARGET}
AFL_NO_FORKSRV=1 $AFLPATH/afl-fuzz -i ${DIR_FUZZ_IN}/${FUZZ_TARGET} -o ${AFLOUT}/${FUZZ_TARGET} -m52 -- src/test/fuzz/${FUZZ_TARGET}
```

## libFuzzer

A recent version of `clang`, the address sanitizer and libFuzzer is needed (all
found in the `compiler-rt` runtime libraries package).

To build all fuzz targets with libFuzzer, run

```
./configure --enable-fuzz --with-sanitizers=fuzzer,address CC=clang CXX=clang++
make
```

See https://llvm.org/docs/LibFuzzer.html#running on how to run the libFuzzer
instrumented executable.

Alternatively, you can run the script through the fuzzing test harness (only
libFuzzer supported so far). You need to pass it the inputs directory and
the specific test target you want to run.

```
./test/fuzz/test_runner.py ${DIR_FUZZ_IN} bech32
```

### macOS hints for libFuzzer

The default clang/llvm version supplied by Apple on macOS does not include
fuzzing libraries, so macOS users will need to install a full version, for
example using `brew install llvm`.

Should you run into problems with the address sanitizer, it is possible you
may need to run `./configure` with `--disable-asm` to avoid errors
with certain assembly code from PIVX Core's code. See [developer notes on sanitizers](https://github.com/PIVX-Project/PIVX/blob/master/doc/developer-notes.md#sanitizers)
for more information.

You may also need to take care of giving the correct path for clang and
clang++, like `CC=/path/to/clang CXX=/path/to/clang++` if the non-systems
clang does not come first in your path.

Full configure that was tested on macOS Catalina with `brew` installed `llvm`:
```
./configure --enable-fuzz --with-sanitizers=fuzzer,address,undefined CC=/usr/local/opt/llvm/bin/clang CXX=/usr/local/opt/llvm/bin/clang++ --disable-asm
```
Loading

0 comments on commit 44b5327

Please sign in to comment.