From a4153e20eca3f3a7bec5f0b1f13d0ae07557416b Mon Sep 17 00:00:00 2001 From: Patrick Strateman Date: Fri, 22 Apr 2016 15:19:33 -0700 Subject: [PATCH 1/2] Simple fuzzing framework --- .gitignore | 1 + src/Makefile.am | 1 + src/Makefile.test.include | 21 +++ src/test/test_bitcoin_fuzzy.cpp | 256 ++++++++++++++++++++++++++++++++ 4 files changed, 279 insertions(+) create mode 100644 src/test/test_bitcoin_fuzzy.cpp diff --git a/.gitignore b/.gitignore index c765ffb60..5dbce8aa7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ src/bitcoind src/bitcoin-cli src/bitcoin-tx src/test/test_bitcoin +src/test/test_bitcoin_fuzzy src/qt/test/test_bitcoin-qt # autoreconf diff --git a/src/Makefile.am b/src/Makefile.am index 8c12aee21..389be6c05 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -61,6 +61,7 @@ EXTRA_LIBRARIES += \ lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS) bin_PROGRAMS = +noinst_PROGRAMS = TESTS = BENCHMARKS = diff --git a/src/Makefile.test.include b/src/Makefile.test.include index a14adc787..190c69be5 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -4,6 +4,7 @@ TESTS += test/test_bitcoin bin_PROGRAMS += test/test_bitcoin +noinst_PROGRAMS += test/test_bitcoin_fuzzy TEST_SRCDIR = test TEST_BINARY=test/test_bitcoin$(EXEEXT) @@ -38,6 +39,7 @@ RAW_TEST_FILES = GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h) +# test_bitcoin binary # BITCOIN_TESTS =\ test/arith_uint256_tests.cpp \ test/scriptnum10.h \ @@ -119,6 +121,25 @@ test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -s if ENABLE_ZMQ test_test_bitcoin_LDADD += $(ZMQ_LIBS) endif +# + +# test_bitcoin_fuzzy binary # +test_test_bitcoin_fuzzy_SOURCES = test/test_bitcoin_fuzzy.cpp +test_test_bitcoin_fuzzy_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_test_bitcoin_fuzzy_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_test_bitcoin_fuzzy_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) + +test_test_bitcoin_fuzzy_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBSECP256K1) + +test_test_bitcoin_fuzzy_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) +# nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES) diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp new file mode 100644 index 000000000..e1abde16f --- /dev/null +++ b/src/test/test_bitcoin_fuzzy.cpp @@ -0,0 +1,256 @@ +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif + +#include "consensus/merkle.h" +#include "primitives/block.h" +#include "script/script.h" +#include "addrman.h" +#include "chain.h" +#include "coins.h" +#include "compressor.h" +#include "net.h" +#include "protocol.h" +#include "streams.h" +#include "undo.h" +#include "version.h" + +#include +#include + +#include +#include + +enum TEST_ID { + CBLOCK_DESERIALIZE=0, + CTRANSACTION_DESERIALIZE, + CBLOCKLOCATOR_DESERIALIZE, + CBLOCKMERKLEROOT, + CADDRMAN_DESERIALIZE, + CBLOCKHEADER_DESERIALIZE, + CBANENTRY_DESERIALIZE, + CTXUNDO_DESERIALIZE, + CBLOCKUNDO_DESERIALIZE, + CCOINS_DESERIALIZE, + CNETADDR_DESERIALIZE, + CSERVICE_DESERIALIZE, + CMESSAGEHEADER_DESERIALIZE, + CADDRESS_DESERIALIZE, + CINV_DESERIALIZE, + CBLOOMFILTER_DESERIALIZE, + CDISKBLOCKINDEX_DESERIALIZE, + CTXOUTCOMPRESSOR_DESERIALIZE, + TEST_ID_END +}; + +bool read_stdin(std::vector &data) { + char buffer[1024]; + ssize_t length=0; + while((length = read(STDIN_FILENO, buffer, 1024)) > 0) { + data.insert(data.end(), buffer, buffer+length); + + if (data.size() > (1<<20)) return false; + } + return length==0; +} + +int main(int argc, char **argv) +{ + std::vector buffer; + if (!read_stdin(buffer)) return 0; + + if (buffer.size() < sizeof(uint32_t)) return 0; + + uint32_t test_id = 0xffffffff; + memcpy(&test_id, &buffer[0], sizeof(uint32_t)); + buffer.erase(buffer.begin(), buffer.begin() + sizeof(uint32_t)); + + if (test_id >= TEST_ID_END) return 0; + + CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); + try { + int nVersion; + ds >> nVersion; + ds.SetVersion(nVersion); + } catch (const std::ios_base::failure& e) { + return 0; + } + + switch(test_id) { + case CBLOCK_DESERIALIZE: + { + try + { + CBlock block; + ds >> block; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CTRANSACTION_DESERIALIZE: + { + try + { + CTransaction tx(deserialize, ds); + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CBLOCKLOCATOR_DESERIALIZE: + { + try + { + CBlockLocator bl; + ds >> bl; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CBLOCKMERKLEROOT: + { + try + { + CBlock block; + ds >> block; + bool mutated; + BlockMerkleRoot(block, &mutated); + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CADDRMAN_DESERIALIZE: + { + try + { + CAddrMan am; + ds >> am; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CBLOCKHEADER_DESERIALIZE: + { + try + { + CBlockHeader bh; + ds >> bh; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CBANENTRY_DESERIALIZE: + { + try + { + CBanEntry be; + ds >> be; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CTXUNDO_DESERIALIZE: + { + try + { + CTxUndo tu; + ds >> tu; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CBLOCKUNDO_DESERIALIZE: + { + try + { + CBlockUndo bu; + ds >> bu; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CCOINS_DESERIALIZE: + { + try + { + CCoins block; + ds >> block; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CNETADDR_DESERIALIZE: + { + try + { + CNetAddr na; + ds >> na; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CSERVICE_DESERIALIZE: + { + try + { + CService s; + ds >> s; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CMESSAGEHEADER_DESERIALIZE: + { + CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00}; + try + { + CMessageHeader mh(pchMessageStart); + ds >> mh; + if (!mh.IsValid(pchMessageStart)) {return 0;} + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CADDRESS_DESERIALIZE: + { + try + { + CAddress a; + ds >> a; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CINV_DESERIALIZE: + { + try + { + CInv i; + ds >> i; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CBLOOMFILTER_DESERIALIZE: + { + try + { + CBloomFilter bf; + ds >> bf; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CDISKBLOCKINDEX_DESERIALIZE: + { + try + { + CDiskBlockIndex dbi; + ds >> dbi; + } catch (const std::ios_base::failure& e) {return 0;} + break; + } + case CTXOUTCOMPRESSOR_DESERIALIZE: + { + CTxOut to; + try + { + ds >> to; + } catch (const std::ios_base::failure& e) {return 0;} + + CTxOutCompressor toc(to); + break; + } + default: + return 0; + } + return 0; +} + From 8b15434b59c6cd7368b4db680544cd77ed337bd3 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 16 Nov 2016 12:25:18 +0100 Subject: [PATCH 2/2] doc: Add bare-bones documentation for fuzzing --- doc/README.md | 1 + doc/fuzzing.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 doc/fuzzing.md diff --git a/doc/README.md b/doc/README.md index 8b9c0ea26..36684e540 100644 --- a/doc/README.md +++ b/doc/README.md @@ -68,6 +68,7 @@ The Bitcoin repo's [root README](/README.md) contains relevant information on th ### Miscellaneous - [Assets Attribution](assets-attribution.md) - [Files](files.md) +- [Fuzz-testing](fuzzing.md) - [Reduce Traffic](reduce-traffic.md) - [Tor Support](tor.md) - [Init Scripts (systemd/upstart/openrc)](init.md) diff --git a/doc/fuzzing.md b/doc/fuzzing.md new file mode 100644 index 000000000..bf3ad1786 --- /dev/null +++ b/doc/fuzzing.md @@ -0,0 +1,66 @@ +Fuzz-testing Bitcoin Core +========================== + +A special test harness `test_bitcoin_fuzzy` is provided to provide an easy +entry point for fuzzers and the like. In this document we'll describe how to +use it with 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- +make +export AFLPATH=$PWD +``` + +Instrumentation +---------------- + +To build Bitcoin Core using AFL instrumentation (this assumes that the +`AFLPATH` was set as above): +``` +./configure --disable-ccache --disable-shared --enable-tests CC=${AFLPATH}/afl-gcc CXX=${AFLPATH}/afl-g++ +export AFL_HARDEN=1 +cd src/ +make test/test_bitcoin_fuzzy +``` +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. + +Preparing fuzzing +------------------ + +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. + +``` +mkdir inputs +AFLIN=$PWD/inputs +mkdir outputs +AFLOUT=$PWD/outputs +``` + +Example inputs are available from: + +- https://download.visucore.com/bitcoin/bitcoin_fuzzy_in.tar.xz +- http://strateman.ninja/fuzzing.tar.xz + +Extract these (or other starting inputs) into the `inputs` directory before starting fuzzing. + +Fuzzing +-------- + +To start the actual fuzzing use: +``` +$AFLPATH/afl-fuzz -i ${AFLIN} -o ${AFLOUT} -m52 -- test/test_bitcoin_fuzzy +``` + +You may have to change a few kernel parameters to test optimally - `afl-fuzz` +will print an error and suggestion if so. +