From 11753e64e770f6161c7fc016d1dc5d9602ba6cbb Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com> Date: Sat, 26 Aug 2023 20:38:35 +0530 Subject: [PATCH] merge bitcoin#19259: Add fuzzing harness for LoadMempool(...) and DumpMempool(...) --- src/Makefile.test.include | 3 +- src/test/fuzz/util.h | 23 ++++++++++++++- src/test/fuzz/validation_load_mempool.cpp | 34 +++++++++++++++++++++++ src/validation.cpp | 10 +++---- src/validation.h | 6 ++-- 5 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 src/test/fuzz/validation_load_mempool.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 754897ae2e..2af4cf91c6 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -321,7 +321,8 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/timedata.cpp \ test/fuzz/transaction.cpp \ test/fuzz/tx_in.cpp \ - test/fuzz/tx_out.cpp + test/fuzz/tx_out.cpp \ + test/fuzz/validation_load_mempool.cpp endif # ENABLE_FUZZ_BINARY nodist_test_test_dash_SOURCES = $(GENERATED_TEST_FILES) diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index 7bacf104f9..ef48cf290e 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -248,6 +248,16 @@ void SetFuzzedErrNo(FuzzedDataProvider& fuzzed_data_provider, const std::array(0, 133); +} + /** * Returns a byte vector of specified size regardless of the number of remaining bytes available * from the fuzzer. Pads with zero value bytes if needed to achieve the specified size. @@ -347,6 +357,7 @@ public: FILE* open() { + SetFuzzedErrNo(m_fuzzed_data_provider); if (m_fuzzed_data_provider.ConsumeBool()) { return nullptr; } @@ -388,6 +399,7 @@ public: static ssize_t read(void* cookie, char* buf, size_t size) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; } @@ -406,6 +418,7 @@ public: static ssize_t write(void* cookie, const char* buf, size_t size) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange(0, size); if (AdditionOverflow(static_cast(fuzzed_file->m_offset), n)) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; @@ -416,8 +429,9 @@ public: static int seek(void* cookie, int64_t* offset, int whence) { - assert(whence == SEEK_SET || whence == SEEK_CUR); // SEEK_END not implemented yet. + assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END); FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); int64_t new_offset = 0; if (whence == SEEK_SET) { new_offset = *offset; @@ -426,6 +440,12 @@ public: return -1; } new_offset = fuzzed_file->m_offset + *offset; + } else if (whence == SEEK_END) { + const int64_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange(0, 4096); + if (AdditionOverflow(n, *offset)) { + return -1; + } + new_offset = n + *offset; } if (new_offset < 0) { return -1; @@ -438,6 +458,7 @@ public: static int close(void* cookie) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange(-1, 0); } }; diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp new file mode 100644 index 0000000000..e1a21b6c53 --- /dev/null +++ b/src/test/fuzz/validation_load_mempool.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2020 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +void initialize_validation_load_mempool() +{ + static const auto testing_setup = MakeNoLogFileContext(); +} + +FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); + FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider); + + CTxMemPool pool{}; + auto fuzzed_fopen = [&](const fs::path&, const char*) { + return fuzzed_file_provider.open(); + }; + (void)LoadMempool(pool, ::ChainstateActive(), fuzzed_fopen); + (void)DumpMempool(pool, fuzzed_fopen, true); +} diff --git a/src/validation.cpp b/src/validation.cpp index 54621f3c93..031c950e12 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -5507,11 +5507,11 @@ CBlockFileInfo* GetBlockFileInfo(size_t n) static const uint64_t MEMPOOL_DUMP_VERSION = 1; -bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate) +bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function) { const CChainParams& chainparams = Params(); int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60; - FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb"); + FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat", "rb")}; CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); if (file.IsNull()) { LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n"); @@ -5601,7 +5601,7 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate) return true; } -bool DumpMempool(const CTxMemPool& pool) +bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function, bool skip_file_commit) { int64_t start = GetTimeMicros(); @@ -5624,7 +5624,7 @@ bool DumpMempool(const CTxMemPool& pool) int64_t mid = GetTimeMicros(); try { - FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb"); + FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat.new", "wb")}; if (!filestr) { return false; } @@ -5647,7 +5647,7 @@ bool DumpMempool(const CTxMemPool& pool) LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size()); file << unbroadcast_txids; - if (!FileCommit(file.Get())) + if (!skip_file_commit && !FileCommit(file.Get())) throw std::runtime_error("FileCommit failed"); file.fclose(); if (!RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat")) { diff --git a/src/validation.h b/src/validation.h index 3e5f067aa7..fdbd7ea4ab 100644 --- a/src/validation.h +++ b/src/validation.h @@ -1087,11 +1087,13 @@ bool GetBlockHash(uint256& hashRet, int nBlockHeight = -1); /** Get block file info entry for one block file */ CBlockFileInfo* GetBlockFileInfo(size_t n); +using FopenFn = std::function; + /** Dump the mempool to disk. */ -bool DumpMempool(const CTxMemPool& pool); +bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function = fsbridge::fopen, bool skip_file_commit = false); /** Load the mempool from disk. */ -bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate); +bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function = fsbridge::fopen); //! Check whether the block associated with this index entry is pruned or not. inline bool IsBlockPruned(const CBlockIndex* pblockindex)