partial bitcoin#19143: Add fuzzing harnesses for CAutoFile, CBufferedFile, LoadExternalBlockFile and other FILE* consumers

This commit is contained in:
Kittywhiskers Van Gogh 2022-07-15 21:23:01 +05:30
parent da690542f4
commit eb2fa81288
5 changed files with 408 additions and 0 deletions

View File

@ -12,6 +12,7 @@ FUZZ_TARGETS = \
test/fuzz/addrman_deserialize \
test/fuzz/asmap \
test/fuzz/asmap_direct \
test/fuzz/autofile \
test/fuzz/banentry_deserialize \
test/fuzz/banman \
test/fuzz/base_encode_decode \
@ -31,6 +32,7 @@ FUZZ_TARGETS = \
test/fuzz/blockundo_deserialize \
test/fuzz/bloom_filter \
test/fuzz/bloomfilter_deserialize \
test/fuzz/buffered_file \
test/fuzz/checkqueue \
test/fuzz/coins_deserialize \
test/fuzz/coins_view \
@ -51,6 +53,7 @@ FUZZ_TARGETS = \
test/fuzz/key \
test/fuzz/key_io \
test/fuzz/key_origin_info_deserialize \
test/fuzz/load_external_block_file \
test/fuzz/locale \
test/fuzz/merkle_block_deserialize \
test/fuzz/merkleblock \
@ -340,6 +343,12 @@ test_fuzz_asmap_direct_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_asmap_direct_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(LDFLAGS_WRAP_EXCEPTIONS)
test_fuzz_asmap_direct_SOURCES = $(FUZZ_SUITE) test/fuzz/asmap_direct.cpp
test_fuzz_autofile_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_autofile_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_autofile_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_autofile_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(LDFLAGS_WRAP_EXCEPTIONS)
test_fuzz_autofile_SOURCES = $(FUZZ_SUITE) test/fuzz/autofile.cpp
test_fuzz_banentry_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBANENTRY_DESERIALIZE=1
test_fuzz_banentry_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_banentry_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
@ -454,6 +463,12 @@ test_fuzz_bloomfilter_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_bloomfilter_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(LDFLAGS_WRAP_EXCEPTIONS)
test_fuzz_bloomfilter_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_buffered_file_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_buffered_file_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_buffered_file_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_buffered_file_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(LDFLAGS_WRAP_EXCEPTIONS)
test_fuzz_buffered_file_SOURCES = $(FUZZ_SUITE) test/fuzz/buffered_file.cpp
test_fuzz_checkqueue_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_checkqueue_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_checkqueue_LDADD = $(FUZZ_SUITE_LD_COMMON)
@ -574,6 +589,12 @@ test_fuzz_key_origin_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_key_origin_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(LDFLAGS_WRAP_EXCEPTIONS)
test_fuzz_key_origin_info_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_load_external_block_file_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_load_external_block_file_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_load_external_block_file_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_load_external_block_file_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(LDFLAGS_WRAP_EXCEPTIONS)
test_fuzz_load_external_block_file_SOURCES = $(FUZZ_SUITE) test/fuzz/load_external_block_file.cpp
test_fuzz_locale_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_locale_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_locale_LDADD = $(FUZZ_SUITE_LD_COMMON)

View File

@ -0,0 +1,72 @@
// 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 <optional.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <array>
#include <cstdint>
#include <iostream>
#include <optional>
#include <string>
#include <vector>
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider);
CAutoFile auto_file = fuzzed_auto_file_provider.open();
while (fuzzed_data_provider.ConsumeBool()) {
switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) {
case 0: {
std::array<uint8_t, 4096> arr{};
try {
auto_file.read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
} catch (const std::ios_base::failure&) {
}
break;
}
case 1: {
const std::array<uint8_t, 4096> arr{};
try {
auto_file.write((const char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
} catch (const std::ios_base::failure&) {
}
break;
}
case 2: {
try {
auto_file.ignore(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
} catch (const std::ios_base::failure&) {
}
break;
}
case 3: {
auto_file.fclose();
break;
}
case 4: {
ReadFromStream(fuzzed_data_provider, auto_file);
break;
}
case 5: {
WriteToStream(fuzzed_data_provider, auto_file);
break;
}
}
}
(void)auto_file.Get();
(void)auto_file.GetType();
(void)auto_file.GetVersion();
(void)auto_file.IsNull();
if (fuzzed_data_provider.ConsumeBool()) {
FILE* f = auto_file.release();
if (f != nullptr) {
fclose(f);
}
}
}

View File

@ -0,0 +1,78 @@
// 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 <optional.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <array>
#include <cstdint>
#include <iostream>
#include <optional>
#include <string>
#include <vector>
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider);
std::optional<CBufferedFile> opt_buffered_file;
FILE* fuzzed_file = fuzzed_file_provider.open();
try {
opt_buffered_file.emplace(fuzzed_file, fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096), fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096), fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeIntegral<int>());
} catch (const std::ios_base::failure&) {
if (fuzzed_file != nullptr) {
fclose(fuzzed_file);
}
}
if (opt_buffered_file && fuzzed_file != nullptr) {
bool setpos_fail = false;
while (fuzzed_data_provider.ConsumeBool()) {
switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) {
case 0: {
std::array<uint8_t, 4096> arr{};
try {
opt_buffered_file->read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
} catch (const std::ios_base::failure&) {
}
break;
}
case 1: {
opt_buffered_file->Seek(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096));
break;
}
case 2: {
opt_buffered_file->SetLimit(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096));
break;
}
case 3: {
if (!opt_buffered_file->SetPos(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096))) {
setpos_fail = true;
}
break;
}
case 4: {
if (setpos_fail) {
// Calling FindByte(...) after a failed SetPos(...) call may result in an infinite loop.
break;
}
try {
opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral<char>());
} catch (const std::ios_base::failure&) {
}
break;
}
case 5: {
ReadFromStream(fuzzed_data_provider, *opt_buffered_file);
break;
}
}
}
opt_buffered_file->GetPos();
opt_buffered_file->GetType();
opt_buffered_file->GetVersion();
}
}

View File

@ -0,0 +1,31 @@
// 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 <chainparams.h>
#include <flatfile.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/setup_common.h>
#include <validation.h>
#include <cstdint>
#include <vector>
void initialize()
{
InitializeFuzzingContext();
}
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider);
FILE* fuzzed_block_file = fuzzed_file_provider.open();
if (fuzzed_block_file == nullptr) {
return;
}
FlatFilePos flat_file_pos;
LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr);
}

View File

@ -23,6 +23,7 @@
#include <version.h>
#include <cstdint>
#include <cstdio>
#include <string>
#include <vector>
@ -194,4 +195,209 @@ void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::
static const BasicTestingSetup basic_testing_setup{chain_name, {"-nodebuglogfile"}};
}
class FuzzedFileProvider
{
FuzzedDataProvider& m_fuzzed_data_provider;
int64_t m_offset = 0;
public:
FuzzedFileProvider(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider}
{
}
FILE* open()
{
if (m_fuzzed_data_provider.ConsumeBool()) {
return nullptr;
}
std::string mode;
switch (m_fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) {
case 0: {
mode = "r";
break;
}
case 1: {
mode = "r+";
break;
}
case 2: {
mode = "w";
break;
}
case 3: {
mode = "w+";
break;
}
case 4: {
mode = "a";
break;
}
case 5: {
mode = "a+";
break;
}
}
#ifdef _GNU_SOURCE
const cookie_io_functions_t io_hooks = {
FuzzedFileProvider::read,
FuzzedFileProvider::write,
FuzzedFileProvider::seek,
FuzzedFileProvider::close,
};
return fopencookie(this, mode.c_str(), io_hooks);
#else
(void)mode;
return nullptr;
#endif
}
static ssize_t read(void* cookie, char* buf, size_t size)
{
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) {
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
}
const std::vector<uint8_t> random_bytes = fuzzed_file->m_fuzzed_data_provider.ConsumeBytes<uint8_t>(size);
if (random_bytes.empty()) {
return 0;
}
std::memcpy(buf, random_bytes.data(), random_bytes.size());
if (AdditionOverflow((uint64_t)fuzzed_file->m_offset, random_bytes.size())) {
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
}
fuzzed_file->m_offset += random_bytes.size();
return random_bytes.size();
}
static ssize_t write(void* cookie, const char* buf, size_t size)
{
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size);
if (AdditionOverflow(fuzzed_file->m_offset, n)) {
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
}
fuzzed_file->m_offset += n;
return n;
}
static int seek(void* cookie, int64_t* offset, int whence)
{
assert(whence == SEEK_SET || whence == SEEK_CUR); // SEEK_END not implemented yet.
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
int64_t new_offset = 0;
if (whence == SEEK_SET) {
new_offset = *offset;
} else if (whence == SEEK_CUR) {
if (AdditionOverflow(fuzzed_file->m_offset, *offset)) {
return -1;
}
new_offset = fuzzed_file->m_offset + *offset;
}
if (new_offset < 0) {
return -1;
}
fuzzed_file->m_offset = new_offset;
*offset = new_offset;
return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
}
static int close(void* cookie)
{
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
}
};
[[ nodiscard ]] inline FuzzedFileProvider ConsumeFile(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
return {fuzzed_data_provider};
}
class FuzzedAutoFileProvider
{
FuzzedDataProvider& m_fuzzed_data_provider;
FuzzedFileProvider m_fuzzed_file_provider;
public:
FuzzedAutoFileProvider(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider}, m_fuzzed_file_provider{fuzzed_data_provider}
{
}
CAutoFile open()
{
return {m_fuzzed_file_provider.open(), m_fuzzed_data_provider.ConsumeIntegral<int>(), m_fuzzed_data_provider.ConsumeIntegral<int>()};
}
};
[[ nodiscard ]] inline FuzzedAutoFileProvider ConsumeAutoFile(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
return {fuzzed_data_provider};
}
#define WRITE_TO_STREAM_CASE(id, type, consume) \
case id: { \
type o = consume; \
stream << o; \
break; \
}
template <typename Stream>
void WriteToStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noexcept
{
while (fuzzed_data_provider.ConsumeBool()) {
try {
switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 13)) {
WRITE_TO_STREAM_CASE(0, bool, fuzzed_data_provider.ConsumeBool())
WRITE_TO_STREAM_CASE(1, char, fuzzed_data_provider.ConsumeIntegral<char>())
WRITE_TO_STREAM_CASE(2, int8_t, fuzzed_data_provider.ConsumeIntegral<int8_t>())
WRITE_TO_STREAM_CASE(3, uint8_t, fuzzed_data_provider.ConsumeIntegral<uint8_t>())
WRITE_TO_STREAM_CASE(4, int16_t, fuzzed_data_provider.ConsumeIntegral<int16_t>())
WRITE_TO_STREAM_CASE(5, uint16_t, fuzzed_data_provider.ConsumeIntegral<uint16_t>())
WRITE_TO_STREAM_CASE(6, int32_t, fuzzed_data_provider.ConsumeIntegral<int32_t>())
WRITE_TO_STREAM_CASE(7, uint32_t, fuzzed_data_provider.ConsumeIntegral<uint32_t>())
WRITE_TO_STREAM_CASE(8, int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>())
WRITE_TO_STREAM_CASE(9, uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>())
WRITE_TO_STREAM_CASE(10, float, fuzzed_data_provider.ConsumeFloatingPoint<float>())
WRITE_TO_STREAM_CASE(11, double, fuzzed_data_provider.ConsumeFloatingPoint<double>())
WRITE_TO_STREAM_CASE(12, std::string, fuzzed_data_provider.ConsumeRandomLengthString(32))
WRITE_TO_STREAM_CASE(13, std::vector<char>, ConsumeRandomLengthIntegralVector<char>(fuzzed_data_provider))
}
} catch (const std::ios_base::failure&) {
break;
}
}
}
#define READ_FROM_STREAM_CASE(id, type) \
case id: { \
type o; \
stream >> o; \
break; \
}
template <typename Stream>
void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noexcept
{
while (fuzzed_data_provider.ConsumeBool()) {
try {
switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 13)) {
READ_FROM_STREAM_CASE(0, bool)
READ_FROM_STREAM_CASE(1, char)
READ_FROM_STREAM_CASE(2, int8_t)
READ_FROM_STREAM_CASE(3, uint8_t)
READ_FROM_STREAM_CASE(4, int16_t)
READ_FROM_STREAM_CASE(5, uint16_t)
READ_FROM_STREAM_CASE(6, int32_t)
READ_FROM_STREAM_CASE(7, uint32_t)
READ_FROM_STREAM_CASE(8, int64_t)
READ_FROM_STREAM_CASE(9, uint64_t)
READ_FROM_STREAM_CASE(10, float)
READ_FROM_STREAM_CASE(11, double)
READ_FROM_STREAM_CASE(12, std::string)
READ_FROM_STREAM_CASE(13, std::vector<char>)
}
} catch (const std::ios_base::failure&) {
break;
}
}
}
#endif // BITCOIN_TEST_FUZZ_UTIL_H