Skip to content

Commit 5bc209c

Browse files
committed
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)
2 parents b83264d + 8b15434 commit 5bc209c

File tree

6 files changed

+346
-0
lines changed

6 files changed

+346
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ src/bitcoind
66
src/bitcoin-cli
77
src/bitcoin-tx
88
src/test/test_bitcoin
9+
src/test/test_bitcoin_fuzzy
910
src/qt/test/test_bitcoin-qt
1011

1112
# autoreconf

doc/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ The Bitcoin repo's [root README](/README.md) contains relevant information on th
6868
### Miscellaneous
6969
- [Assets Attribution](assets-attribution.md)
7070
- [Files](files.md)
71+
- [Fuzz-testing](fuzzing.md)
7172
- [Reduce Traffic](reduce-traffic.md)
7273
- [Tor Support](tor.md)
7374
- [Init Scripts (systemd/upstart/openrc)](init.md)

doc/fuzzing.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
Fuzz-testing Bitcoin Core
2+
==========================
3+
4+
A special test harness `test_bitcoin_fuzzy` is provided to provide an easy
5+
entry point for fuzzers and the like. In this document we'll describe how to
6+
use it with AFL.
7+
8+
Building AFL
9+
-------------
10+
11+
It is recommended to always use the latest version of afl:
12+
```
13+
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
14+
tar -zxvf afl-latest.tgz
15+
cd afl-<version>
16+
make
17+
export AFLPATH=$PWD
18+
```
19+
20+
Instrumentation
21+
----------------
22+
23+
To build Bitcoin Core using AFL instrumentation (this assumes that the
24+
`AFLPATH` was set as above):
25+
```
26+
./configure --disable-ccache --disable-shared --enable-tests CC=${AFLPATH}/afl-gcc CXX=${AFLPATH}/afl-g++
27+
export AFL_HARDEN=1
28+
cd src/
29+
make test/test_bitcoin_fuzzy
30+
```
31+
We disable ccache because we don't want to pollute the ccache with instrumented
32+
objects, and similarly don't want to use non-instrumented cached objects linked
33+
in.
34+
35+
Preparing fuzzing
36+
------------------
37+
38+
AFL needs an input directory with examples, and an output directory where it
39+
will place examples that it found. These can be anywhere in the file system,
40+
we'll define environment variables to make it easy to reference them.
41+
42+
```
43+
mkdir inputs
44+
AFLIN=$PWD/inputs
45+
mkdir outputs
46+
AFLOUT=$PWD/outputs
47+
```
48+
49+
Example inputs are available from:
50+
51+
- https://download.visucore.com/bitcoin/bitcoin_fuzzy_in.tar.xz
52+
- http://strateman.ninja/fuzzing.tar.xz
53+
54+
Extract these (or other starting inputs) into the `inputs` directory before starting fuzzing.
55+
56+
Fuzzing
57+
--------
58+
59+
To start the actual fuzzing use:
60+
```
61+
$AFLPATH/afl-fuzz -i ${AFLIN} -o ${AFLOUT} -m52 -- test/test_bitcoin_fuzzy
62+
```
63+
64+
You may have to change a few kernel parameters to test optimally - `afl-fuzz`
65+
will print an error and suggestion if so.
66+

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ EXTRA_LIBRARIES += \
6161
lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS)
6262

6363
bin_PROGRAMS =
64+
noinst_PROGRAMS =
6465
TESTS =
6566
BENCHMARKS =
6667

src/Makefile.test.include

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
TESTS += test/test_bitcoin
66
bin_PROGRAMS += test/test_bitcoin
7+
noinst_PROGRAMS += test/test_bitcoin_fuzzy
78
TEST_SRCDIR = test
89
TEST_BINARY=test/test_bitcoin$(EXEEXT)
910

@@ -38,6 +39,7 @@ RAW_TEST_FILES =
3839

3940
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
4041

42+
# test_bitcoin binary #
4143
BITCOIN_TESTS =\
4244
test/arith_uint256_tests.cpp \
4345
test/scriptnum10.h \
@@ -120,6 +122,25 @@ test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -s
120122
if ENABLE_ZMQ
121123
test_test_bitcoin_LDADD += $(ZMQ_LIBS)
122124
endif
125+
#
126+
127+
# test_bitcoin_fuzzy binary #
128+
test_test_bitcoin_fuzzy_SOURCES = test/test_bitcoin_fuzzy.cpp
129+
test_test_bitcoin_fuzzy_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
130+
test_test_bitcoin_fuzzy_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
131+
test_test_bitcoin_fuzzy_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
132+
133+
test_test_bitcoin_fuzzy_LDADD = \
134+
$(LIBUNIVALUE) \
135+
$(LIBBITCOIN_SERVER) \
136+
$(LIBBITCOIN_COMMON) \
137+
$(LIBBITCOIN_UTIL) \
138+
$(LIBBITCOIN_CONSENSUS) \
139+
$(LIBBITCOIN_CRYPTO) \
140+
$(LIBSECP256K1)
141+
142+
test_test_bitcoin_fuzzy_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
143+
#
123144

124145
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
125146

src/test/test_bitcoin_fuzzy.cpp

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
// Copyright (c) 2009-2015 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#if defined(HAVE_CONFIG_H)
6+
#include "config/bitcoin-config.h"
7+
#endif
8+
9+
#include "consensus/merkle.h"
10+
#include "primitives/block.h"
11+
#include "script/script.h"
12+
#include "addrman.h"
13+
#include "chain.h"
14+
#include "coins.h"
15+
#include "compressor.h"
16+
#include "net.h"
17+
#include "protocol.h"
18+
#include "streams.h"
19+
#include "undo.h"
20+
#include "version.h"
21+
22+
#include <stdint.h>
23+
#include <unistd.h>
24+
25+
#include <algorithm>
26+
#include <vector>
27+
28+
enum TEST_ID {
29+
CBLOCK_DESERIALIZE=0,
30+
CTRANSACTION_DESERIALIZE,
31+
CBLOCKLOCATOR_DESERIALIZE,
32+
CBLOCKMERKLEROOT,
33+
CADDRMAN_DESERIALIZE,
34+
CBLOCKHEADER_DESERIALIZE,
35+
CBANENTRY_DESERIALIZE,
36+
CTXUNDO_DESERIALIZE,
37+
CBLOCKUNDO_DESERIALIZE,
38+
CCOINS_DESERIALIZE,
39+
CNETADDR_DESERIALIZE,
40+
CSERVICE_DESERIALIZE,
41+
CMESSAGEHEADER_DESERIALIZE,
42+
CADDRESS_DESERIALIZE,
43+
CINV_DESERIALIZE,
44+
CBLOOMFILTER_DESERIALIZE,
45+
CDISKBLOCKINDEX_DESERIALIZE,
46+
CTXOUTCOMPRESSOR_DESERIALIZE,
47+
TEST_ID_END
48+
};
49+
50+
bool read_stdin(std::vector<char> &data) {
51+
char buffer[1024];
52+
ssize_t length=0;
53+
while((length = read(STDIN_FILENO, buffer, 1024)) > 0) {
54+
data.insert(data.end(), buffer, buffer+length);
55+
56+
if (data.size() > (1<<20)) return false;
57+
}
58+
return length==0;
59+
}
60+
61+
int main(int argc, char **argv)
62+
{
63+
std::vector<char> buffer;
64+
if (!read_stdin(buffer)) return 0;
65+
66+
if (buffer.size() < sizeof(uint32_t)) return 0;
67+
68+
uint32_t test_id = 0xffffffff;
69+
memcpy(&test_id, &buffer[0], sizeof(uint32_t));
70+
buffer.erase(buffer.begin(), buffer.begin() + sizeof(uint32_t));
71+
72+
if (test_id >= TEST_ID_END) return 0;
73+
74+
CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
75+
try {
76+
int nVersion;
77+
ds >> nVersion;
78+
ds.SetVersion(nVersion);
79+
} catch (const std::ios_base::failure& e) {
80+
return 0;
81+
}
82+
83+
switch(test_id) {
84+
case CBLOCK_DESERIALIZE:
85+
{
86+
try
87+
{
88+
CBlock block;
89+
ds >> block;
90+
} catch (const std::ios_base::failure& e) {return 0;}
91+
break;
92+
}
93+
case CTRANSACTION_DESERIALIZE:
94+
{
95+
try
96+
{
97+
CTransaction tx(deserialize, ds);
98+
} catch (const std::ios_base::failure& e) {return 0;}
99+
break;
100+
}
101+
case CBLOCKLOCATOR_DESERIALIZE:
102+
{
103+
try
104+
{
105+
CBlockLocator bl;
106+
ds >> bl;
107+
} catch (const std::ios_base::failure& e) {return 0;}
108+
break;
109+
}
110+
case CBLOCKMERKLEROOT:
111+
{
112+
try
113+
{
114+
CBlock block;
115+
ds >> block;
116+
bool mutated;
117+
BlockMerkleRoot(block, &mutated);
118+
} catch (const std::ios_base::failure& e) {return 0;}
119+
break;
120+
}
121+
case CADDRMAN_DESERIALIZE:
122+
{
123+
try
124+
{
125+
CAddrMan am;
126+
ds >> am;
127+
} catch (const std::ios_base::failure& e) {return 0;}
128+
break;
129+
}
130+
case CBLOCKHEADER_DESERIALIZE:
131+
{
132+
try
133+
{
134+
CBlockHeader bh;
135+
ds >> bh;
136+
} catch (const std::ios_base::failure& e) {return 0;}
137+
break;
138+
}
139+
case CBANENTRY_DESERIALIZE:
140+
{
141+
try
142+
{
143+
CBanEntry be;
144+
ds >> be;
145+
} catch (const std::ios_base::failure& e) {return 0;}
146+
break;
147+
}
148+
case CTXUNDO_DESERIALIZE:
149+
{
150+
try
151+
{
152+
CTxUndo tu;
153+
ds >> tu;
154+
} catch (const std::ios_base::failure& e) {return 0;}
155+
break;
156+
}
157+
case CBLOCKUNDO_DESERIALIZE:
158+
{
159+
try
160+
{
161+
CBlockUndo bu;
162+
ds >> bu;
163+
} catch (const std::ios_base::failure& e) {return 0;}
164+
break;
165+
}
166+
case CCOINS_DESERIALIZE:
167+
{
168+
try
169+
{
170+
CCoins block;
171+
ds >> block;
172+
} catch (const std::ios_base::failure& e) {return 0;}
173+
break;
174+
}
175+
case CNETADDR_DESERIALIZE:
176+
{
177+
try
178+
{
179+
CNetAddr na;
180+
ds >> na;
181+
} catch (const std::ios_base::failure& e) {return 0;}
182+
break;
183+
}
184+
case CSERVICE_DESERIALIZE:
185+
{
186+
try
187+
{
188+
CService s;
189+
ds >> s;
190+
} catch (const std::ios_base::failure& e) {return 0;}
191+
break;
192+
}
193+
case CMESSAGEHEADER_DESERIALIZE:
194+
{
195+
CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00};
196+
try
197+
{
198+
CMessageHeader mh(pchMessageStart);
199+
ds >> mh;
200+
if (!mh.IsValid(pchMessageStart)) {return 0;}
201+
} catch (const std::ios_base::failure& e) {return 0;}
202+
break;
203+
}
204+
case CADDRESS_DESERIALIZE:
205+
{
206+
try
207+
{
208+
CAddress a;
209+
ds >> a;
210+
} catch (const std::ios_base::failure& e) {return 0;}
211+
break;
212+
}
213+
case CINV_DESERIALIZE:
214+
{
215+
try
216+
{
217+
CInv i;
218+
ds >> i;
219+
} catch (const std::ios_base::failure& e) {return 0;}
220+
break;
221+
}
222+
case CBLOOMFILTER_DESERIALIZE:
223+
{
224+
try
225+
{
226+
CBloomFilter bf;
227+
ds >> bf;
228+
} catch (const std::ios_base::failure& e) {return 0;}
229+
break;
230+
}
231+
case CDISKBLOCKINDEX_DESERIALIZE:
232+
{
233+
try
234+
{
235+
CDiskBlockIndex dbi;
236+
ds >> dbi;
237+
} catch (const std::ios_base::failure& e) {return 0;}
238+
break;
239+
}
240+
case CTXOUTCOMPRESSOR_DESERIALIZE:
241+
{
242+
CTxOut to;
243+
try
244+
{
245+
ds >> to;
246+
} catch (const std::ios_base::failure& e) {return 0;}
247+
248+
CTxOutCompressor toc(to);
249+
break;
250+
}
251+
default:
252+
return 0;
253+
}
254+
return 0;
255+
}
256+

0 commit comments

Comments
 (0)