diff --git a/src/test/fuzz/CMakeLists.txt b/src/test/fuzz/CMakeLists.txt --- a/src/test/fuzz/CMakeLists.txt +++ b/src/test/fuzz/CMakeLists.txt @@ -84,12 +84,14 @@ addrdb asmap asmap_direct + autofile banman base_encode_decode block block_header blockfilter bloom_filter + buffered_file cashaddr chain checkqueue @@ -116,6 +118,7 @@ key key_io kitchen_sink + load_external_block_file locale merkleblock message diff --git a/src/test/fuzz/autofile.cpp b/src/test/fuzz/autofile.cpp new file mode 100644 --- /dev/null +++ b/src/test/fuzz/autofile.cpp @@ -0,0 +1,79 @@ +// 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 + +void test_one_input(const std::vector &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(0, 5)) { + case 0: { + std::array arr{}; + try { + auto_file.read( + (char *)arr.data(), + fuzzed_data_provider.ConsumeIntegralInRange( + 0, 4096)); + } catch (const std::ios_base::failure &) { + } + break; + } + case 1: { + const std::array arr{}; + try { + auto_file.write( + (const char *)arr.data(), + fuzzed_data_provider.ConsumeIntegralInRange( + 0, 4096)); + } catch (const std::ios_base::failure &) { + } + break; + } + case 2: { + try { + auto_file.ignore( + fuzzed_data_provider.ConsumeIntegralInRange( + 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); + } + } +} diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp new file mode 100644 --- /dev/null +++ b/src/test/fuzz/buffered_file.cpp @@ -0,0 +1,93 @@ +// 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 test_one_input(const std::vector &buffer) { + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider); + std::optional opt_buffered_file; + FILE *fuzzed_file = fuzzed_file_provider.open(); + try { + opt_buffered_file.emplace( + fuzzed_file, + fuzzed_data_provider.ConsumeIntegralInRange(0, 4096), + fuzzed_data_provider.ConsumeIntegralInRange(0, 4096), + fuzzed_data_provider.ConsumeIntegral(), + fuzzed_data_provider.ConsumeIntegral()); + } 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(0, 5)) { + case 0: { + std::array arr{}; + try { + opt_buffered_file->read( + (char *)arr.data(), + fuzzed_data_provider.ConsumeIntegralInRange( + 0, 4096)); + } catch (const std::ios_base::failure &) { + } + break; + } + case 1: { + opt_buffered_file->Seek( + fuzzed_data_provider.ConsumeIntegralInRange( + 0, 4096)); + break; + } + case 2: { + opt_buffered_file->SetLimit( + fuzzed_data_provider.ConsumeIntegralInRange( + 0, 4096)); + break; + } + case 3: { + if (!opt_buffered_file->SetPos( + fuzzed_data_provider + .ConsumeIntegralInRange(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()); + } 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(); + } +} diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp new file mode 100644 --- /dev/null +++ b/src/test/fuzz/load_external_block_file.cpp @@ -0,0 +1,32 @@ +// 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 + +void initialize() { + InitializeFuzzingContext(); +} + +void test_one_input(const std::vector &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(GetConfig(), fuzzed_block_file, + fuzzed_data_provider.ConsumeBool() ? &flat_file_pos + : nullptr); +} diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -259,4 +260,231 @@ {"-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(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 random_bytes = + fuzzed_file->m_fuzzed_data_provider.ConsumeBytes(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( + 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) { + // SEEK_END not implemented yet. + assert(whence == SEEK_SET || whence == SEEK_CUR); + 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( + -1, 0); + } + + static int close(void *cookie) { + FuzzedFileProvider *fuzzed_file = (FuzzedFileProvider *)cookie; + return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange( + -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(), + m_fuzzed_data_provider.ConsumeIntegral()}; + } +}; + +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 +void WriteToStream(FuzzedDataProvider &fuzzed_data_provider, + Stream &stream) noexcept { + while (fuzzed_data_provider.ConsumeBool()) { + try { + switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 13)) { + WRITE_TO_STREAM_CASE(0, bool, + fuzzed_data_provider.ConsumeBool()) + WRITE_TO_STREAM_CASE( + 1, char, fuzzed_data_provider.ConsumeIntegral()) + WRITE_TO_STREAM_CASE( + 2, int8_t, fuzzed_data_provider.ConsumeIntegral()) + WRITE_TO_STREAM_CASE( + 3, uint8_t, fuzzed_data_provider.ConsumeIntegral()) + WRITE_TO_STREAM_CASE( + 4, int16_t, fuzzed_data_provider.ConsumeIntegral()) + WRITE_TO_STREAM_CASE( + 5, uint16_t, + fuzzed_data_provider.ConsumeIntegral()) + WRITE_TO_STREAM_CASE( + 6, int32_t, fuzzed_data_provider.ConsumeIntegral()) + WRITE_TO_STREAM_CASE( + 7, uint32_t, + fuzzed_data_provider.ConsumeIntegral()) + WRITE_TO_STREAM_CASE( + 8, int64_t, fuzzed_data_provider.ConsumeIntegral()) + WRITE_TO_STREAM_CASE( + 9, uint64_t, + fuzzed_data_provider.ConsumeIntegral()) + WRITE_TO_STREAM_CASE( + 10, float, + fuzzed_data_provider.ConsumeFloatingPoint()) + WRITE_TO_STREAM_CASE( + 11, double, + fuzzed_data_provider.ConsumeFloatingPoint()) + WRITE_TO_STREAM_CASE( + 12, std::string, + fuzzed_data_provider.ConsumeRandomLengthString(32)) + WRITE_TO_STREAM_CASE(13, std::vector, + ConsumeRandomLengthIntegralVector( + 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 +void ReadFromStream(FuzzedDataProvider &fuzzed_data_provider, + Stream &stream) noexcept { + while (fuzzed_data_provider.ConsumeBool()) { + try { + switch (fuzzed_data_provider.ConsumeIntegralInRange(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) + } + } catch (const std::ios_base::failure &) { + break; + } + } +} + #endif // BITCOIN_TEST_FUZZ_UTIL_H