Skip to content

Commit 67ec26c

Browse files
author
MarcoFalke
committed
Merge #19259: fuzz: Add fuzzing harness for LoadMempool(...) and DumpMempool(...)
68afd3e tests: Add fuzzing harness for LoadMempool(...) and DumpMempool(...) (practicalswift) 91af6b9 validation: Make DumpMempool(...) and LoadMempool(...) easier to test/fuzz/mock (practicalswift) af322c7 tests: Set errno in FuzzedFileProvider. Implement seek(..., ..., SEEK_END). (practicalswift) Pull request description: Add fuzzing harness for `LoadMempool(...)` and `DumpMempool(...)`. See [`doc/fuzzing.md`](https://github.com/bitcoin/bitcoin/blob/master/doc/fuzzing.md) for information on how to fuzz Bitcoin Core. Don't forget to contribute any coverage increasing inputs you find to the [Bitcoin Core fuzzing corpus repo](https://github.com/bitcoin-core/qa-assets). Happy fuzzing :) ACKs for top commit: jonatack: Tested re-ACK 68afd3e Tree-SHA512: 4b5fcaa87e6eb478611d3b68eb6859645a5e121e7e3b056ad2815699dace0a6123706ff542def371b47f4ab3ce2b8a29782026d84fb505827121e9b4cc7dac31
2 parents 3ba195a + 68afd3e commit 67ec26c

File tree

5 files changed

+67
-9
lines changed

5 files changed

+67
-9
lines changed

src/Makefile.test.include

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,8 @@ test_fuzz_fuzz_SOURCES = \
297297
test/fuzz/transaction.cpp \
298298
test/fuzz/tx_in.cpp \
299299
test/fuzz/tx_out.cpp \
300-
test/fuzz/txrequest.cpp
300+
test/fuzz/txrequest.cpp \
301+
test/fuzz/validation_load_mempool.cpp
301302
endif # ENABLE_FUZZ_BINARY
302303

303304
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)

src/test/fuzz/util.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,16 @@ void SetFuzzedErrNo(FuzzedDataProvider& fuzzed_data_provider, const std::array<T
259259
errno = fuzzed_data_provider.PickValueInArray(errnos);
260260
}
261261

262+
/*
263+
* Sets a fuzzed errno in the range [0, 133 (EHWPOISON)]. Can be used from functions emulating
264+
* standard library functions that set errno, or in other contexts where the value of errno
265+
* might be relevant for the execution path that will be taken.
266+
*/
267+
inline void SetFuzzedErrNo(FuzzedDataProvider& fuzzed_data_provider) noexcept
268+
{
269+
errno = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 133);
270+
}
271+
262272
/**
263273
* Returns a byte vector of specified size regardless of the number of remaining bytes available
264274
* from the fuzzer. Pads with zero value bytes if needed to achieve the specified size.
@@ -345,6 +355,7 @@ class FuzzedFileProvider
345355

346356
FILE* open()
347357
{
358+
SetFuzzedErrNo(m_fuzzed_data_provider);
348359
if (m_fuzzed_data_provider.ConsumeBool()) {
349360
return nullptr;
350361
}
@@ -386,6 +397,7 @@ class FuzzedFileProvider
386397
static ssize_t read(void* cookie, char* buf, size_t size)
387398
{
388399
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
400+
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
389401
if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) {
390402
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
391403
}
@@ -404,6 +416,7 @@ class FuzzedFileProvider
404416
static ssize_t write(void* cookie, const char* buf, size_t size)
405417
{
406418
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
419+
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
407420
const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size);
408421
if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)n)) {
409422
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
@@ -414,8 +427,9 @@ class FuzzedFileProvider
414427

415428
static int seek(void* cookie, int64_t* offset, int whence)
416429
{
417-
assert(whence == SEEK_SET || whence == SEEK_CUR); // SEEK_END not implemented yet.
430+
assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
418431
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
432+
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
419433
int64_t new_offset = 0;
420434
if (whence == SEEK_SET) {
421435
new_offset = *offset;
@@ -424,6 +438,12 @@ class FuzzedFileProvider
424438
return -1;
425439
}
426440
new_offset = fuzzed_file->m_offset + *offset;
441+
} else if (whence == SEEK_END) {
442+
const int64_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 4096);
443+
if (AdditionOverflow(n, *offset)) {
444+
return -1;
445+
}
446+
new_offset = n + *offset;
427447
}
428448
if (new_offset < 0) {
429449
return -1;
@@ -436,6 +456,7 @@ class FuzzedFileProvider
436456
static int close(void* cookie)
437457
{
438458
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
459+
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
439460
return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
440461
}
441462
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) 2020 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+
#include <chainparamsbase.h>
6+
#include <test/fuzz/FuzzedDataProvider.h>
7+
#include <test/fuzz/fuzz.h>
8+
#include <test/fuzz/util.h>
9+
#include <test/util/setup_common.h>
10+
#include <txmempool.h>
11+
#include <util/time.h>
12+
#include <validation.h>
13+
14+
#include <cstdint>
15+
#include <vector>
16+
17+
void initialize_validation_load_mempool()
18+
{
19+
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
20+
}
21+
22+
FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool)
23+
{
24+
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
25+
SetMockTime(ConsumeTime(fuzzed_data_provider));
26+
FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider);
27+
28+
CTxMemPool pool{};
29+
auto fuzzed_fopen = [&](const fs::path&, const char*) {
30+
return fuzzed_file_provider.open();
31+
};
32+
(void)LoadMempool(pool, ::ChainstateActive(), fuzzed_fopen);
33+
(void)DumpMempool(pool, fuzzed_fopen, true);
34+
}

src/validation.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5010,11 +5010,11 @@ CBlockFileInfo* GetBlockFileInfo(size_t n)
50105010

50115011
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
50125012

5013-
bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate)
5013+
bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function)
50145014
{
50155015
const CChainParams& chainparams = Params();
50165016
int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
5017-
FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb");
5017+
FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat", "rb")};
50185018
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
50195019
if (file.IsNull()) {
50205020
LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
@@ -5095,7 +5095,7 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate)
50955095
return true;
50965096
}
50975097

5098-
bool DumpMempool(const CTxMemPool& pool)
5098+
bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function, bool skip_file_commit)
50995099
{
51005100
int64_t start = GetTimeMicros();
51015101

@@ -5118,7 +5118,7 @@ bool DumpMempool(const CTxMemPool& pool)
51185118
int64_t mid = GetTimeMicros();
51195119

51205120
try {
5121-
FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb");
5121+
FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat.new", "wb")};
51225122
if (!filestr) {
51235123
return false;
51245124
}
@@ -5141,7 +5141,7 @@ bool DumpMempool(const CTxMemPool& pool)
51415141
LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size());
51425142
file << unbroadcast_txids;
51435143

5144-
if (!FileCommit(file.Get()))
5144+
if (!skip_file_commit && !FileCommit(file.Get()))
51455145
throw std::runtime_error("FileCommit failed");
51465146
file.fclose();
51475147
if (!RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat")) {

src/validation.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,11 +1013,13 @@ int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Para
10131013
/** Get block file info entry for one block file */
10141014
CBlockFileInfo* GetBlockFileInfo(size_t n);
10151015

1016+
using FopenFn = std::function<FILE*(const fs::path&, const char*)>;
1017+
10161018
/** Dump the mempool to disk. */
1017-
bool DumpMempool(const CTxMemPool& pool);
1019+
bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function = fsbridge::fopen, bool skip_file_commit = false);
10181020

10191021
/** Load the mempool from disk. */
1020-
bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate);
1022+
bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function = fsbridge::fopen);
10211023

10221024
//! Check whether the block associated with this index entry is pruned or not.
10231025
inline bool IsBlockPruned(const CBlockIndex* pblockindex)

0 commit comments

Comments
 (0)