Browse Source
0.148b15434
doc: Add bare-bones documentation for fuzzing (Wladimir J. van der Laan)a4153e2
Simple fuzzing framework (Patrick Strateman)
Wladimir J. van der Laan
8 years ago
6 changed files with 346 additions and 0 deletions
@ -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. |
||||||
|
|
@ -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 new issue