aboutsummaryrefslogtreecommitdiff
path: root/src/test/fuzz
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/fuzz')
-rw-r--r--src/test/fuzz/deserialize.cpp167
-rw-r--r--src/test/fuzz/fuzz.cpp77
-rw-r--r--src/test/fuzz/fuzz.h15
-rw-r--r--src/test/fuzz/script_flags.cpp72
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;
+}