Skip to content

Commit 44b5327

Browse files
committed
Merge #2252: Fuzzing framework support
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
2 parents 2f0d2d0 + d059544 commit 44b5327

15 files changed

+1041
-10
lines changed

.gitignore

+22
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,29 @@ src/pivx-cli
1313
src/pivx-tx
1414
src/bench/bench_pivx
1515
src/test/test_pivx
16+
src/test/test_pivx_fuzzy
1617
src/qt/test/test_pivx-qt
18+
src/test/fuzz/address_deserialize
19+
src/test/fuzz/addrman_deserialize
20+
src/test/fuzz/banentry_deserialize
21+
src/test/fuzz/block_deserialize
22+
src/test/fuzz/blockheader_deserialize
23+
src/test/fuzz/blocklocator_deserialize
24+
src/test/fuzz/blockmerkleroot
25+
src/test/fuzz/blockundo_deserialize
26+
src/test/fuzz/bloomfilter_deserialize
27+
src/test/fuzz/coins_deserialize
28+
src/test/fuzz/diskblockindex_deserialize
29+
src/test/fuzz/inv_deserialize
30+
src/test/fuzz/messageheader_deserialize
31+
src/test/fuzz/netaddr_deserialize
32+
src/test/fuzz/service_deserialize
33+
src/test/fuzz/transaction_deserialize
34+
src/test/fuzz/txoutcompressor_deserialize
35+
src/test/fuzz/txundo_deserialize
36+
src/test/fuzz/transaction
37+
src/test/fuzz/*deserialize
38+
1739

1840
# autoreconf
1941
Makefile.in

.travis/test_04_install.sh

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
export LC_ALL=C.UTF-8
88

99
travis_retry docker pull "$DOCKER_NAME_TAG"
10+
11+
export DIR_FUZZ_IN=${TRAVIS_BUILD_DIR}/qa-assets
12+
git clone https://github.com/bitcoin-core/qa-assets ${DIR_FUZZ_IN}
13+
export DIR_FUZZ_IN=${DIR_FUZZ_IN}/fuzz_seed_corpus/
14+
1015
env | grep -E '^(BITCOIN_CONFIG|CCACHE_|WINEDEBUG|LC_ALL|BOOST_TEST_RANDOM|CONFIG_SHELL)' | tee /tmp/env
1116
if [[ $HOST = *-mingw32 ]]; then
1217
DOCKER_ADMIN="--cap-add SYS_ADMIN"

.travis/test_06_script_b.sh

+6
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,10 @@ if [ "$RUN_FUNCTIONAL_TESTS" = "true" ]; then
2626
END_FOLD
2727
fi
2828

29+
if [ "$RUN_FUZZ_TESTS" = "true" ]; then
30+
BEGIN_FOLD fuzz-tests
31+
DOCKER_EXEC test/fuzz/test_runner.py -l DEBUG ${DIR_FUZZ_IN}
32+
END_FOLD
33+
fi
34+
2935
cd ${TRAVIS_BUILD_DIR} || (echo "could not enter travis build dir $TRAVIS_BUILD_DIR"; exit 1)

Makefile.am

+2-1
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ dist_noinst_SCRIPTS = autogen.sh
247247
EXTRA_DIST = $(DIST_SHARE) $(DIST_CONTRIB) $(DIST_DOCS) $(DIST_CARGO) $(WINDOWS_PACKAGING) $(LINUX_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS)
248248

249249
EXTRA_DIST += \
250-
test/functional
250+
test/functional \
251+
test/fuzz
251252

252253
EXTRA_DIST += \
253254
test/util/bitcoin-util-test.py \

configure.ac

+40-7
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ AC_ARG_WITH([params-dir],
122122
],
123123
[params_path=""])
124124

125-
# Enable wallet
126125
AC_ARG_ENABLE([wallet],
127126
[AS_HELP_STRING([--disable-wallet],
128127
[disable wallet (enabled by default)])],
@@ -167,6 +166,12 @@ AC_ARG_ENABLE([extended-functional-tests],
167166
[use_extended_functional_tests=$enableval],
168167
[use_extended_functional_tests=no])
169168

169+
AC_ARG_ENABLE([fuzz],
170+
AS_HELP_STRING([--enable-fuzz],
171+
[enable building of fuzz targets (default no). enabling this will disable all other targets]),
172+
[enable_fuzz=$enableval],
173+
[enable_fuzz=no])
174+
170175
AC_ARG_WITH([qtcharts],
171176
[AS_HELP_STRING([--with-qtcharts],
172177
[enable qtcharts support (default is yes if qt is enabled and qtchartview is found)])],
@@ -314,7 +319,14 @@ if test x$use_sanitizers != x; then
314319
AX_CHECK_LINK_FLAG(
315320
[[-fsanitize=$use_sanitizers]],
316321
[[SANITIZER_LDFLAGS=-fsanitize=$use_sanitizers]],
317-
[AC_MSG_ERROR([linker did not accept requested flags, you are missing required libraries])])
322+
[AC_MSG_ERROR([linker did not accept requested flags, you are missing required libraries])],
323+
[],
324+
[AC_LANG_PROGRAM([[
325+
#include <cstdint>
326+
#include <cstddef>
327+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { return 0; }
328+
__attribute__((weak)) // allow for libFuzzer linking
329+
]],[[]])])
318330
fi
319331

320332
ERROR_CXXFLAGS=
@@ -996,6 +1008,28 @@ AC_SUBST(LEVELDB_CPPFLAGS)
9961008
AC_SUBST(LIBLEVELDB)
9971009
AC_SUBST(LIBMEMENV)
9981010

1011+
dnl enable-fuzz should disable all other targets
1012+
if test "x$enable_fuzz" = "xyes"; then
1013+
AC_MSG_WARN(enable-fuzz will disable other targets)
1014+
build_bitcoin_utils=no
1015+
build_bitcoin_cli=no
1016+
build_bitcoin_tx=no
1017+
build_bitcoind=no
1018+
build_bitcoin_libs=no
1019+
bitcoin_enable_qt=no
1020+
bitcoin_enable_qt_test=no
1021+
bitcoin_enable_qt_dbus=no
1022+
enable_wallet=yes # needs to be built for now.
1023+
use_bench=no
1024+
use_upnp=no
1025+
use_zmq=no
1026+
else
1027+
BITCOIN_QT_INIT
1028+
1029+
dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus
1030+
BITCOIN_QT_CONFIGURE([$use_pkgconfig])
1031+
fi
1032+
9991033
if test x$enable_wallet != xno; then
10001034
dnl Check for libdb_cxx only if wallet enabled
10011035
BITCOIN_FIND_BDB48
@@ -1030,11 +1064,6 @@ if test x$have_miniupnpc != xno; then
10301064
fi
10311065
fi
10321066

1033-
BITCOIN_QT_INIT
1034-
1035-
dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus
1036-
BITCOIN_QT_CONFIGURE([$use_pkgconfig])
1037-
10381067
if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononono; then
10391068
use_boost=no
10401069
else
@@ -1409,6 +1438,7 @@ AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows])
14091438
AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes])
14101439
AM_CONDITIONAL([ENABLE_MINING_RPC],[test x$enable_mining_rpc = xyes])
14111440
AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes])
1441+
AM_CONDITIONAL([ENABLE_FUZZ],[test x$enable_fuzz = xyes])
14121442
AM_CONDITIONAL([ENABLE_QT],[test x$bitcoin_enable_qt = xyes])
14131443
AM_CONDITIONAL([ENABLE_QT_TESTS],[test x$BUILD_TEST_QT = xyes])
14141444
AM_CONDITIONAL([ENABLE_BENCH],[test x$use_bench = xyes])
@@ -1556,6 +1586,9 @@ if test x$bitcoin_enable_qt != xno; then
15561586
fi
15571587
echo " with zmq = $use_zmq"
15581588
echo " with test = $use_tests"
1589+
if test x$use_tests != xno; then
1590+
echo " with fuzz = $enable_fuzz"
1591+
fi
15591592
echo " with bench = $use_bench"
15601593
echo " with upnp = $use_upnp"
15611594
echo " with params = $params_path"

doc/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ The PIVX repo's [root README](/README.md) contains relevant information on the d
6464
### Miscellaneous
6565
- [Assets Attribution](assets-attribution.md)
6666
- [Files](files.md)
67+
- [Fuzz-testing](fuzzing.md)
6768
- [Reduce Memory](reduce-memory.md)
6869
- [Tor Support](tor.md)
6970
- [Init Scripts (systemd/upstart/openrc)](init.md)

doc/developer-notes.md

+42
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,48 @@ make cov
261261
# A coverage report will now be accessible at `./test_pivx.coverage/index.html`.
262262
```
263263

264+
**Sanitizers**
265+
PIVX can be compiled with various "sanitizers" enabled, which add
266+
instrumentation for issues regarding things like memory safety, thread race
267+
conditions, or undefined behavior. This is controlled with the
268+
`--with-sanitizers` configure flag, which should be a comma separated list of
269+
sanitizers to enable. The sanitizer list should correspond to supported
270+
`-fsanitize=` options in your compiler. These sanitizers have runtime overhead,
271+
so they are most useful when testing changes or producing debugging builds.
272+
Some examples:
273+
```bash
274+
# Enable both the address sanitizer and the undefined behavior sanitizer
275+
./configure --with-sanitizers=address,undefined
276+
# Enable the thread sanitizer
277+
./configure --with-sanitizers=thread
278+
```
279+
If you are compiling with GCC you will typically need to install corresponding
280+
"san" libraries to actually compile with these flags, e.g. libasan for the
281+
address sanitizer, libtsan for the thread sanitizer, and libubsan for the
282+
undefined sanitizer. If you are missing required libraries, the configure script
283+
will fail with a linker error when testing the sanitizer flags.
284+
The test suite should pass cleanly with the `thread` and `undefined` sanitizers,
285+
but there are a number of known problems when using the `address` sanitizer. The
286+
address sanitizer is known to fail in
287+
[sha256_sse4::Transform](/src/crypto/sha256_sse4.cpp) which makes it unusable
288+
unless you also use `--disable-asm` when running configure. We would like to fix
289+
sanitizer issues, so please send pull requests if you can fix any errors found
290+
by the address sanitizer (or any other sanitizer).
291+
Not all sanitizer options can be enabled at the same time, e.g. trying to build
292+
with `--with-sanitizers=address,thread` will fail in the configure script as
293+
these sanitizers are mutually incompatible. Refer to your compiler manual to
294+
learn more about these options and which sanitizers are supported by your
295+
compiler.
296+
Additional resources:
297+
* [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html)
298+
* [LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html)
299+
* [MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html)
300+
* [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html)
301+
* [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)
302+
* [GCC Instrumentation Options](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html)
303+
* [Google Sanitizers Wiki](https://github.com/google/sanitizers/wiki)
304+
* [Issue #12691: Enable -fsanitize flags in Travis](https://github.com/bitcoin/bitcoin/issues/12691)
305+
264306
Locking/mutex usage notes
265307
-------------------------
266308

doc/fuzzing.md

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
Fuzz-testing PIVX Core
2+
==========================
3+
4+
A special test harness in `src/test/fuzz/` is provided for each fuzz target to
5+
provide an easy entry point for fuzzers and the like. In this document we'll
6+
describe how to use it with AFL and libFuzzer.
7+
8+
## Preparing fuzzing
9+
10+
The fuzzer needs some inputs to work on, but the inputs or seeds can be used
11+
interchangeably between libFuzzer and AFL.
12+
13+
Extract the example seeds (or other starting inputs) into the inputs
14+
directory before starting fuzzing.
15+
16+
```
17+
git clone https://github.com/bitcoin-core/qa-assets
18+
export DIR_FUZZ_IN=$PWD/qa-assets/fuzz_seed_corpus
19+
```
20+
21+
AFL needs an input directory with examples, and an output directory where it
22+
will place examples that it found. These can be anywhere in the file system,
23+
we'll define environment variables to make it easy to reference them.
24+
25+
So, only for AFL you need to configure the outputs path:
26+
27+
```
28+
mkdir outputs
29+
export AFLOUT=$PWD/outputs
30+
```
31+
32+
libFuzzer will use the input directory as output directory.
33+
34+
## AFL
35+
36+
### Building AFL
37+
38+
It is recommended to always use the latest version of afl:
39+
```
40+
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
41+
tar -zxvf afl-latest.tgz
42+
cd afl-<version>
43+
make
44+
export AFLPATH=$PWD
45+
```
46+
47+
For macOS you may need to ignore x86 compilation checks when running `make`:
48+
`AFL_NO_X86=1 make`.
49+
50+
### Instrumentation
51+
52+
To build PIVX Core using AFL instrumentation (this assumes that the
53+
`AFLPATH` was set as above):
54+
```
55+
./configure --disable-shared --enable-tests --enable-fuzz CC=${AFLPATH}/afl-gcc CXX=${AFLPATH}/afl-g++
56+
export AFL_HARDEN=1
57+
make
58+
```
59+
60+
If you are using clang you will need to substitute `afl-gcc` with `afl-clang`
61+
and `afl-g++` with `afl-clang++`, so the first line above becomes:
62+
```
63+
./configure --disable-shared --enable-tests --enable-fuzz CC=${AFLPATH}/afl-clang CXX=${AFLPATH}/afl-clang++
64+
```
65+
66+
We disable ccache because we don't want to pollute the ccache with instrumented
67+
objects, and similarly don't want to use non-instrumented cached objects linked
68+
in.
69+
70+
The fuzzing can be sped up significantly (~200x) by using `afl-clang-fast` and
71+
`afl-clang-fast++` in place of `afl-gcc` and `afl-g++` when compiling. When
72+
compiling using `afl-clang-fast`/`afl-clang-fast++` the resulting
73+
binary will be instrumented in such a way that the AFL
74+
features "persistent mode" and "deferred forkserver" can be used. See
75+
https://github.com/google/AFL/tree/master/llvm_mode for details.
76+
77+
### Fuzzing
78+
79+
To start the actual fuzzing use:
80+
81+
```
82+
export FUZZ_TARGET=bech32 # Pick a fuzz_target
83+
mkdir ${AFLOUT}/${FUZZ_TARGET}
84+
$AFLPATH/afl-fuzz -i ${DIR_FUZZ_IN}/${FUZZ_TARGET} -o ${AFLOUT}/${FUZZ_TARGET} -m52 -- src/test/fuzz/${FUZZ_TARGET}
85+
```
86+
87+
You may have to change a few kernel parameters to test optimally - `afl-fuzz`
88+
will print an error and suggestion if so.
89+
90+
On macOS you may need to set `AFL_NO_FORKSRV=1` to get the target to run.
91+
```
92+
export FUZZ_TARGET=bech32 # Pick a fuzz_target
93+
mkdir ${AFLOUT}/${FUZZ_TARGET}
94+
AFL_NO_FORKSRV=1 $AFLPATH/afl-fuzz -i ${DIR_FUZZ_IN}/${FUZZ_TARGET} -o ${AFLOUT}/${FUZZ_TARGET} -m52 -- src/test/fuzz/${FUZZ_TARGET}
95+
```
96+
97+
## libFuzzer
98+
99+
A recent version of `clang`, the address sanitizer and libFuzzer is needed (all
100+
found in the `compiler-rt` runtime libraries package).
101+
102+
To build all fuzz targets with libFuzzer, run
103+
104+
```
105+
./configure --enable-fuzz --with-sanitizers=fuzzer,address CC=clang CXX=clang++
106+
make
107+
```
108+
109+
See https://llvm.org/docs/LibFuzzer.html#running on how to run the libFuzzer
110+
instrumented executable.
111+
112+
Alternatively, you can run the script through the fuzzing test harness (only
113+
libFuzzer supported so far). You need to pass it the inputs directory and
114+
the specific test target you want to run.
115+
116+
```
117+
./test/fuzz/test_runner.py ${DIR_FUZZ_IN} bech32
118+
```
119+
120+
### macOS hints for libFuzzer
121+
122+
The default clang/llvm version supplied by Apple on macOS does not include
123+
fuzzing libraries, so macOS users will need to install a full version, for
124+
example using `brew install llvm`.
125+
126+
Should you run into problems with the address sanitizer, it is possible you
127+
may need to run `./configure` with `--disable-asm` to avoid errors
128+
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)
129+
for more information.
130+
131+
You may also need to take care of giving the correct path for clang and
132+
clang++, like `CC=/path/to/clang CXX=/path/to/clang++` if the non-systems
133+
clang does not come first in your path.
134+
135+
Full configure that was tested on macOS Catalina with `brew` installed `llvm`:
136+
```
137+
./configure --enable-fuzz --with-sanitizers=fuzzer,address,undefined CC=/usr/local/opt/llvm/bin/clang CXX=/usr/local/opt/llvm/bin/clang++ --disable-asm
138+
```

0 commit comments

Comments
 (0)