diff options
Diffstat (limited to 'src/test/fuzz')
-rw-r--r-- | src/test/fuzz/deserialize.cpp | 167 | ||||
-rw-r--r-- | src/test/fuzz/fuzz.cpp | 77 | ||||
-rw-r--r-- | src/test/fuzz/fuzz.h | 15 | ||||
-rw-r--r-- | src/test/fuzz/script_flags.cpp | 72 |
4 files changed, 331 insertions, 0 deletions
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp new file mode 100644 index 0000000000..97d7633715 --- /dev/null +++ b/src/test/fuzz/deserialize.cpp @@ -0,0 +1,167 @@ +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <addrman.h> +#include <blockencodings.h> +#include <chain.h> +#include <coins.h> +#include <compressor.h> +#include <consensus/merkle.h> +#include <net.h> +#include <primitives/block.h> +#include <protocol.h> +#include <pubkey.h> +#include <script/script.h> +#include <streams.h> +#include <undo.h> +#include <version.h> + +#include <stdint.h> +#include <unistd.h> + +#include <algorithm> +#include <memory> +#include <vector> + +#include <test/fuzz/fuzz.h> + +void test_one_input(std::vector<uint8_t> buffer) +{ + CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); + try { + int nVersion; + ds >> nVersion; + ds.SetVersion(nVersion); + } catch (const std::ios_base::failure& e) { + return; + } + +#if BLOCK_DESERIALIZE + try + { + CBlock block; + ds >> block; + } catch (const std::ios_base::failure& e) {return;} +#elif TRANSACTION_DESERIALIZE + try + { + CTransaction tx(deserialize, ds); + } catch (const std::ios_base::failure& e) {return;} +#elif BLOCKLOCATOR_DESERIALIZE + try + { + CBlockLocator bl; + ds >> bl; + } catch (const std::ios_base::failure& e) {return;} +#elif BLOCKMERKLEROOT + try + { + CBlock block; + ds >> block; + bool mutated; + BlockMerkleRoot(block, &mutated); + } catch (const std::ios_base::failure& e) {return;} +#elif ADDRMAN_DESERIALIZE + try + { + CAddrMan am; + ds >> am; + } catch (const std::ios_base::failure& e) {return;} +#elif BLOCKHEADER_DESERIALIZE + try + { + CBlockHeader bh; + ds >> bh; + } catch (const std::ios_base::failure& e) {return;} +#elif BANENTRY_DESERIALIZE + try + { + CBanEntry be; + ds >> be; + } catch (const std::ios_base::failure& e) {return;} +#elif TXUNDO_DESERIALIZE + try + { + CTxUndo tu; + ds >> tu; + } catch (const std::ios_base::failure& e) {return;} +#elif BLOCKUNDO_DESERIALIZE + try + { + CBlockUndo bu; + ds >> bu; + } catch (const std::ios_base::failure& e) {return;} +#elif COINS_DESERIALIZE + try + { + Coin coin; + ds >> coin; + } catch (const std::ios_base::failure& e) {return;} +#elif NETADDR_DESERIALIZE + try + { + CNetAddr na; + ds >> na; + } catch (const std::ios_base::failure& e) {return;} +#elif SERVICE_DESERIALIZE + try + { + CService s; + ds >> s; + } catch (const std::ios_base::failure& e) {return;} +#elif MESSAGEHEADER_DESERIALIZE + CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00}; + try + { + CMessageHeader mh(pchMessageStart); + ds >> mh; + if (!mh.IsValid(pchMessageStart)) {return;} + } catch (const std::ios_base::failure& e) {return;} +#elif ADDRESS_DESERIALIZE + try + { + CAddress a; + ds >> a; + } catch (const std::ios_base::failure& e) {return;} +#elif INV_DESERIALIZE + try + { + CInv i; + ds >> i; + } catch (const std::ios_base::failure& e) {return;} +#elif BLOOMFILTER_DESERIALIZE + try + { + CBloomFilter bf; + ds >> bf; + } catch (const std::ios_base::failure& e) {return;} +#elif DISKBLOCKINDEX_DESERIALIZE + try + { + CDiskBlockIndex dbi; + ds >> dbi; + } catch (const std::ios_base::failure& e) {return;} +#elif TXOUTCOMPRESSOR_DESERIALIZE + CTxOut to; + CTxOutCompressor toc(to); + try + { + ds >> toc; + } catch (const std::ios_base::failure& e) {return;} +#elif BLOCKTRANSACTIONS_DESERIALIZE + try + { + BlockTransactions bt; + ds >> bt; + } catch (const std::ios_base::failure& e) {return;} +#elif BLOCKTRANSACTIONSREQUEST_DESERIALIZE + try + { + BlockTransactionsRequest btr; + ds >> btr; + } catch (const std::ios_base::failure& e) {return;} +#else +#error Need at least one fuzz target to compile +#endif +} diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp new file mode 100644 index 0000000000..0709da5563 --- /dev/null +++ b/src/test/fuzz/fuzz.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <test/fuzz/fuzz.h> + +#include <unistd.h> + +#include <pubkey.h> +#include <util/memory.h> + + +static bool read_stdin(std::vector<uint8_t>& data) +{ + uint8_t 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; +} + +static void initialize() +{ + const static auto verify_handle = MakeUnique<ECCVerifyHandle>(); +} + +// This function is used by libFuzzer +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + test_one_input(std::vector<uint8_t>(data, data + size)); + return 0; +} + +// This function is used by libFuzzer +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) +{ + initialize(); + return 0; +} + +// Disabled under WIN32 due to clash with Cygwin's WinMain. +#ifndef WIN32 +// Declare main(...) "weak" to allow for libFuzzer linking. libFuzzer provides +// the main(...) function. +__attribute__((weak)) +#endif +int main(int argc, char **argv) +{ + initialize(); +#ifdef __AFL_INIT + // Enable AFL deferred forkserver mode. Requires compilation using + // afl-clang-fast++. See fuzzing.md for details. + __AFL_INIT(); +#endif + +#ifdef __AFL_LOOP + // Enable AFL persistent mode. Requires compilation using afl-clang-fast++. + // See fuzzing.md for details. + while (__AFL_LOOP(1000)) { + std::vector<uint8_t> buffer; + if (!read_stdin(buffer)) { + continue; + } + test_one_input(buffer); + } +#else + std::vector<uint8_t> buffer; + if (!read_stdin(buffer)) { + return 0; + } + test_one_input(buffer); +#endif + return 0; +} diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h new file mode 100644 index 0000000000..8b03a7e46e --- /dev/null +++ b/src/test/fuzz/fuzz.h @@ -0,0 +1,15 @@ +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_TEST_FUZZ_FUZZ_H +#define BITCOIN_TEST_FUZZ_FUZZ_H + +#include <functional> +#include <stdint.h> +#include <vector> + + +void test_one_input(std::vector<uint8_t> buffer); + +#endif // BITCOIN_TEST_FUZZ_FUZZ_H diff --git a/src/test/fuzz/script_flags.cpp b/src/test/fuzz/script_flags.cpp new file mode 100644 index 0000000000..2c0bfa360c --- /dev/null +++ b/src/test/fuzz/script_flags.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <script/interpreter.h> +#include <script/script.h> +#include <streams.h> +#include <version.h> + +#include <test/fuzz/fuzz.h> + +/** Flags that are not forbidden by an assert */ +static bool IsValidFlagCombination(unsigned flags); + +void test_one_input(std::vector<uint8_t> buffer) +{ + CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); + try { + int nVersion; + ds >> nVersion; + ds.SetVersion(nVersion); + } catch (const std::ios_base::failure&) { + return; + } + + try { + const CTransaction tx(deserialize, ds); + const PrecomputedTransactionData txdata(tx); + + unsigned int verify_flags; + ds >> verify_flags; + + if (!IsValidFlagCombination(verify_flags)) return; + + unsigned int fuzzed_flags; + ds >> fuzzed_flags; + + for (unsigned i = 0; i < tx.vin.size(); ++i) { + CTxOut prevout; + ds >> prevout; + + const TransactionSignatureChecker checker{&tx, i, prevout.nValue, txdata}; + + ScriptError serror; + const bool ret = VerifyScript(tx.vin.at(i).scriptSig, prevout.scriptPubKey, &tx.vin.at(i).scriptWitness, verify_flags, checker, &serror); + assert(ret == (serror == SCRIPT_ERR_OK)); + + // Verify that removing flags from a passing test or adding flags to a failing test does not change the result + if (ret) { + verify_flags &= ~fuzzed_flags; + } else { + verify_flags |= fuzzed_flags; + } + if (!IsValidFlagCombination(verify_flags)) return; + + ScriptError serror_fuzzed; + const bool ret_fuzzed = VerifyScript(tx.vin.at(i).scriptSig, prevout.scriptPubKey, &tx.vin.at(i).scriptWitness, verify_flags, checker, &serror_fuzzed); + assert(ret_fuzzed == (serror_fuzzed == SCRIPT_ERR_OK)); + + assert(ret_fuzzed == ret); + } + } catch (const std::ios_base::failure&) { + return; + } +} + +static bool IsValidFlagCombination(unsigned flags) +{ + if (flags & SCRIPT_VERIFY_CLEANSTACK && ~flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) return false; + if (flags & SCRIPT_VERIFY_WITNESS && ~flags & SCRIPT_VERIFY_P2SH) return false; + return true; +} |