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 @@ -79,17 +79,20 @@ asmap base_encode_decode block + block_header bloom_filter rolling_bloom_filter cashaddr descriptor_parse eval_script + fee_rate float hex integer key key_io locale + multiplication_overflow net_permissions netaddress p2p_transport_deserializer @@ -106,6 +109,7 @@ script_ops scriptnum_ops spanparsing + string strprintf timedata transaction diff --git a/src/test/fuzz/block_header.cpp b/src/test/fuzz/block_header.cpp new file mode 100644 --- /dev/null +++ b/src/test/fuzz/block_header.cpp @@ -0,0 +1,43 @@ +// 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 <primitives/block.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> +#include <uint256.h> + +#include <cassert> +#include <cstdint> +#include <optional> +#include <string> +#include <vector> + +void test_one_input(const std::vector<uint8_t> &buffer) { + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const std::optional<CBlockHeader> block_header = + ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider); + if (!block_header) { + return; + } + { + const BlockHash hash = block_header->GetHash(); + static const BlockHash blockhash_max( + uint256S("fffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "fffffff")); + assert(hash != blockhash_max); + assert(block_header->GetBlockTime() == block_header->nTime); + assert(block_header->IsNull() == (block_header->nBits == 0)); + } + { + CBlockHeader mut_block_header = *block_header; + mut_block_header.SetNull(); + assert(mut_block_header.IsNull()); + CBlock block{*block_header}; + assert(block.GetBlockHeader().GetHash() == block_header->GetHash()); + (void)block.ToString(); + block.SetNull(); + assert(block.GetBlockHeader().GetHash() == mut_block_header.GetHash()); + } +} diff --git a/src/test/fuzz/fee_rate.cpp b/src/test/fuzz/fee_rate.cpp new file mode 100644 --- /dev/null +++ b/src/test/fuzz/fee_rate.cpp @@ -0,0 +1,40 @@ +// 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 <amount.h> +#include <feerate.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> + +#include <cstdint> +#include <limits> +#include <string> +#include <vector> + +void test_one_input(const std::vector<uint8_t> &buffer) { + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const Amount satoshis_per_k = ConsumeMoney(fuzzed_data_provider); + const CFeeRate fee_rate{satoshis_per_k}; + + (void)fee_rate.GetFeePerK(); + const size_t bytes = fuzzed_data_provider.ConsumeIntegral<size_t>(); + if (bytes <= uint64_t(std::numeric_limits<int64_t>::max())) { + (void)fee_rate.GetFee(bytes); + } + (void)fee_rate.ToString(); + + const Amount another_satoshis_per_k = ConsumeMoney(fuzzed_data_provider); + CFeeRate larger_fee_rate{another_satoshis_per_k}; + larger_fee_rate += fee_rate; + if (satoshis_per_k != Amount::zero() && + another_satoshis_per_k != Amount::zero()) { + assert(fee_rate < larger_fee_rate); + assert(!(fee_rate > larger_fee_rate)); + assert(!(fee_rate == larger_fee_rate)); + assert(fee_rate <= larger_fee_rate); + assert(!(fee_rate >= larger_fee_rate)); + assert(fee_rate != larger_fee_rate); + } +} diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp --- a/src/test/fuzz/integer.cpp +++ b/src/test/fuzz/integer.cpp @@ -11,6 +11,7 @@ #include <core_io.h> #include <crypto/common.h> #include <crypto/siphash.h> +#include <ctime> #include <key_io.h> #include <memusage.h> #include <netbase.h> @@ -35,6 +36,7 @@ #include <test/fuzz/fuzz.h> #include <cassert> +#include <chrono> #include <limits> #include <vector> @@ -139,6 +141,8 @@ assert(parsed_money == i64 * SATOSHI); } } + const std::chrono::seconds seconds{i64}; + assert(count_seconds(seconds) == i64); const arith_uint256 au256 = UintToArith256(u256); assert(ArithToUint256(au256) == u256); diff --git a/src/test/fuzz/multiplication_overflow.cpp b/src/test/fuzz/multiplication_overflow.cpp new file mode 100644 --- /dev/null +++ b/src/test/fuzz/multiplication_overflow.cpp @@ -0,0 +1,42 @@ +// 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 <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> + +#include <cstdint> +#include <string> +#include <vector> + +namespace { +template <typename T> +void TestMultiplicationOverflow(FuzzedDataProvider &fuzzed_data_provider) { + const T i = fuzzed_data_provider.ConsumeIntegral<T>(); + const T j = fuzzed_data_provider.ConsumeIntegral<T>(); + const bool is_multiplication_overflow_custom = MultiplicationOverflow(i, j); + T result_builtin; + const bool is_multiplication_overflow_builtin = + __builtin_mul_overflow(i, j, &result_builtin); + assert(is_multiplication_overflow_custom == + is_multiplication_overflow_builtin); + if (!is_multiplication_overflow_custom) { + assert(i * j == result_builtin); + } +} +} // namespace + +void test_one_input(const std::vector<uint8_t> &buffer) { + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + TestMultiplicationOverflow<int64_t>(fuzzed_data_provider); + TestMultiplicationOverflow<uint64_t>(fuzzed_data_provider); + TestMultiplicationOverflow<int32_t>(fuzzed_data_provider); + TestMultiplicationOverflow<uint32_t>(fuzzed_data_provider); + TestMultiplicationOverflow<int16_t>(fuzzed_data_provider); + TestMultiplicationOverflow<uint16_t>(fuzzed_data_provider); + TestMultiplicationOverflow<char>(fuzzed_data_provider); + TestMultiplicationOverflow<uint8_t>(fuzzed_data_provider); + TestMultiplicationOverflow<signed char>(fuzzed_data_provider); + TestMultiplicationOverflow<bool>(fuzzed_data_provider); +} diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp new file mode 100644 --- /dev/null +++ b/src/test/fuzz/string.cpp @@ -0,0 +1,98 @@ +// 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 <blockfilter.h> +#include <clientversion.h> +#include <logging.h> +#include <netbase.h> +#include <outputtype.h> +#include <rpc/client.h> +#include <rpc/request.h> +#include <rpc/server.h> +#include <rpc/util.h> +#include <script/descriptor.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> +#include <util/error.h> +#include <util/message.h> +#include <util/settings.h> +#include <util/strencodings.h> +#include <util/string.h> +#include <util/system.h> +#include <util/translation.h> +#include <util/url.h> + +#include <cstdint> +#include <string> +#include <vector> + +void test_one_input(const std::vector<uint8_t> &buffer) { + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const std::string random_string_1 = + fuzzed_data_provider.ConsumeRandomLengthString(32); + const std::string random_string_2 = + fuzzed_data_provider.ConsumeRandomLengthString(32); + const std::vector<std::string> random_string_vector = + ConsumeRandomLengthStringVector(fuzzed_data_provider); + + (void)AmountErrMsg(random_string_1, random_string_2); + (void)AmountHighWarn(random_string_1); + BlockFilterType block_filter_type; + (void)BlockFilterTypeByName(random_string_1, block_filter_type); + (void)Capitalize(random_string_1); + (void)CopyrightHolders(random_string_1); + (void)FormatParagraph( + random_string_1, + fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 1000), + fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 1000)); + (void)FormatSubVersion(random_string_1, + fuzzed_data_provider.ConsumeIntegral<int>(), + random_string_vector); + (void)GetDescriptorChecksum(random_string_1); + (void)HelpExampleCli(random_string_1, random_string_2); + (void)HelpExampleRpc(random_string_1, random_string_2); + (void)HelpMessageGroup(random_string_1); + (void)HelpMessageOpt(random_string_1, random_string_2); + ArgsManager argsman; + (void)IsDeprecatedRPCEnabled(argsman, random_string_1); + (void)Join(random_string_vector, random_string_1); + (void)JSONRPCError(fuzzed_data_provider.ConsumeIntegral<int>(), + random_string_1); + const util::Settings settings; + (void)OnlyHasDefaultSectionSetting(settings, random_string_1, + random_string_2); + (void)ParseNetwork(random_string_1); + try { + (void)ParseNonRFCJSONValue(random_string_1); + } catch (const std::runtime_error &) { + } + OutputType output_type; + (void)ParseOutputType(random_string_1, output_type); + (void)ResolveErrMsg(random_string_1, random_string_2); + try { + (void)RPCConvertNamedValues(random_string_1, random_string_vector); + } catch (const std::runtime_error &) { + } + try { + (void)RPCConvertValues(random_string_1, random_string_vector); + } catch (const std::runtime_error &) { + } + (void)SanitizeString(random_string_1); + (void)SanitizeString( + random_string_1, + fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 3)); + (void)ShellEscape(random_string_1); + int port_out; + std::string host_out; + SplitHostPort(random_string_1, port_out, host_out); + (void)TimingResistantEqual(random_string_1, random_string_2); + (void)ToLower(random_string_1); + (void)ToUpper(random_string_1); + (void)TrimString(random_string_1); + (void)TrimString(random_string_1, random_string_2); + (void)urlDecode(random_string_1); + (void)ValidAsCString(random_string_1); + (void)_(random_string_1.c_str()); +} 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 @@ -5,6 +5,7 @@ #ifndef BITCOIN_TEST_FUZZ_UTIL_H #define BITCOIN_TEST_FUZZ_UTIL_H +#include <amount.h> #include <attributes.h> #include <script/script.h> #include <serialize.h> @@ -25,6 +26,20 @@ return {s.begin(), s.end()}; } +NODISCARD inline std::vector<std::string> +ConsumeRandomLengthStringVector(FuzzedDataProvider &fuzzed_data_provider, + size_t max_vector_size = 16, + size_t max_string_length = 16) noexcept { + const size_t n_elements = + fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size); + std::vector<std::string> r; + for (size_t i = 0; i < n_elements; ++i) { + r.push_back( + fuzzed_data_provider.ConsumeRandomLengthString(max_string_length)); + } + return r; +} + template <typename T> NODISCARD inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider &fuzzed_data_provider, @@ -47,6 +62,15 @@ fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, MAX_OPCODE)); } +NODISCARD inline Amount +ConsumeMoney(FuzzedDataProvider &fuzzed_data_provider) noexcept { + // FIXME find a better way to avoid duplicating the MAX_MONEY definition + int64_t maxMoneyAsInt = int64_t(21000000) * int64_t(100000000); + return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, + maxMoneyAsInt) * + SATOSHI; +} + NODISCARD inline CScript ConsumeScript(FuzzedDataProvider &fuzzed_data_provider) noexcept { const std::vector<uint8_t> b = @@ -59,4 +83,25 @@ return CScriptNum{fuzzed_data_provider.ConsumeIntegral<int64_t>()}; } +template <typename T> bool MultiplicationOverflow(T i, T j) { + static_assert(std::is_integral<T>::value, "Integral required."); + if (std::numeric_limits<T>::is_signed) { + if (i > 0) { + if (j > 0) { + return i > (std::numeric_limits<T>::max() / j); + } else { + return j < (std::numeric_limits<T>::min() / i); + } + } else { + if (j > 0) { + return i < (std::numeric_limits<T>::min() / j); + } else { + return i != 0 && (j < (std::numeric_limits<T>::max() / i)); + } + } + } else { + return j != 0 && i > std::numeric_limits<T>::max() / j; + } +} + #endif // BITCOIN_TEST_FUZZ_UTIL_H