Merge #9172: Resurrect pstratem's "Simple fuzzing framework"
8b15434
doc: Add bare-bones documentation for fuzzing (Wladimir J. van der Laan)a4153e2
Simple fuzzing framework (Patrick Strateman)
This commit is contained in:
commit
5bc209c73f
6 changed files with 346 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
66
doc/fuzzing.md
Normal file
66
doc/fuzzing.md
Normal file
|
@ -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-<version>
|
||||
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.
|
||||
|
|
@ -61,6 +61,7 @@ EXTRA_LIBRARIES += \
|
|||
lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS)
|
||||
|
||||
bin_PROGRAMS =
|
||||
noinst_PROGRAMS =
|
||||
TESTS =
|
||||
BENCHMARKS =
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
@ -120,6 +122,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)
|
||||
|
||||
|
|
256
src/test/test_bitcoin_fuzzy.cpp
Normal file
256
src/test/test_bitcoin_fuzzy.cpp
Normal file
|
@ -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 <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
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<char> &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<char> 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;
|
||||
}
|
||||
|
Loading…
Reference in a new issue