From 5b89956eeb76cf8c9717152fbb0928e026fc0087 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Tue, 11 Nov 2025 11:13:43 +0100 Subject: [PATCH] kernel: Allow null arguments for serialized data An empty span constructed from an empty vector may have a null data pointer depending on the implementation. Remove the BITCOINKERNEL_ARG_NONNULL requirement for these arguments and instead handle such null arguments in the implementation. --- src/kernel/bitcoinkernel.cpp | 9 +++++++++ src/kernel/bitcoinkernel.h | 12 ++++++------ src/test/kernel/test_kernel.cpp | 25 +++++++++++++------------ 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp index 240255bbd52..0b9ea5137f2 100644 --- a/src/kernel/bitcoinkernel.cpp +++ b/src/kernel/bitcoinkernel.cpp @@ -497,6 +497,9 @@ struct btck_Txid: Handle {}; btck_Transaction* btck_transaction_create(const void* raw_transaction, size_t raw_transaction_len) { + if (raw_transaction == nullptr && raw_transaction_len != 0) { + return nullptr; + } try { DataStream stream{std::span{reinterpret_cast(raw_transaction), raw_transaction_len}}; return btck_Transaction::create(std::make_shared(deserialize, TX_WITH_WITNESS, stream)); @@ -556,6 +559,9 @@ void btck_transaction_destroy(btck_Transaction* transaction) btck_ScriptPubkey* btck_script_pubkey_create(const void* script_pubkey, size_t script_pubkey_len) { + if (script_pubkey == nullptr && script_pubkey_len != 0) { + return nullptr; + } auto data = std::span{reinterpret_cast(script_pubkey), script_pubkey_len}; return btck_ScriptPubkey::create(data.begin(), data.end()); } @@ -1033,6 +1039,9 @@ int btck_chainstate_manager_import_blocks(btck_ChainstateManager* chainman, cons btck_Block* btck_block_create(const void* raw_block, size_t raw_block_length) { + if (raw_block == nullptr && raw_block_length != 0) { + return nullptr; + } auto block{std::make_shared()}; DataStream stream{std::span{reinterpret_cast(raw_block), raw_block_length}}; diff --git a/src/kernel/bitcoinkernel.h b/src/kernel/bitcoinkernel.h index 4c94b59ff77..93e160cdf6d 100644 --- a/src/kernel/bitcoinkernel.h +++ b/src/kernel/bitcoinkernel.h @@ -469,12 +469,12 @@ typedef uint8_t btck_ChainType; /** * @brief Create a new transaction from the serialized data. * - * @param[in] raw_transaction Non-null. + * @param[in] raw_transaction Serialized transaction. * @param[in] raw_transaction_len Length of the serialized transaction. * @return The transaction, or null on error. */ BITCOINKERNEL_API btck_Transaction* BITCOINKERNEL_WARN_UNUSED_RESULT btck_transaction_create( - const void* raw_transaction, size_t raw_transaction_len) BITCOINKERNEL_ARG_NONNULL(1); + const void* raw_transaction, size_t raw_transaction_len); /** * @brief Copy a transaction. Transactions are reference counted, so this just @@ -567,12 +567,12 @@ BITCOINKERNEL_API void btck_transaction_destroy(btck_Transaction* transaction); /** * @brief Create a script pubkey from serialized data. - * @param[in] script_pubkey Non-null. + * @param[in] script_pubkey Serialized script pubkey. * @param[in] script_pubkey_len Length of the script pubkey data. * @return The script pubkey. */ BITCOINKERNEL_API btck_ScriptPubkey* BITCOINKERNEL_WARN_UNUSED_RESULT btck_script_pubkey_create( - const void* script_pubkey, size_t script_pubkey_len) BITCOINKERNEL_ARG_NONNULL(1); + const void* script_pubkey, size_t script_pubkey_len); /** * @brief Copy a script pubkey. @@ -1111,12 +1111,12 @@ BITCOINKERNEL_API btck_Block* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_read( /** * @brief Parse a serialized raw block into a new block object. * - * @param[in] raw_block Non-null, serialized block. + * @param[in] raw_block Serialized block. * @param[in] raw_block_len Length of the serialized block. * @return The allocated block, or null on error. */ BITCOINKERNEL_API btck_Block* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_create( - const void* raw_block, size_t raw_block_len) BITCOINKERNEL_ARG_NONNULL(1); + const void* raw_block, size_t raw_block_len); /** * @brief Copy a block. Blocks are reference counted, so this just increments diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp index e1c9376d50a..75c9e466ada 100644 --- a/src/test/kernel/test_kernel.cpp +++ b/src/test/kernel/test_kernel.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -395,6 +394,12 @@ BOOST_AUTO_TEST_CASE(btck_transaction_tests) auto tx2{Transaction{tx_data_2}}; CheckHandle(tx, tx2); + auto invalid_data = hex_string_to_byte_vec("012300"); + BOOST_CHECK_THROW(Transaction{invalid_data}, std::runtime_error); + auto empty_data = hex_string_to_byte_vec(""); + BOOST_CHECK_THROW(Transaction{empty_data}, std::runtime_error); + BOOST_CHECK_THROW(Transaction{std::span(static_cast(nullptr), 2)}, std::runtime_error); + BOOST_CHECK_EQUAL(tx.CountOutputs(), 2); BOOST_CHECK_EQUAL(tx.CountInputs(), 1); auto broken_tx_data{std::span{tx_data.begin(), tx_data.begin() + 10}}; @@ -469,6 +474,8 @@ BOOST_AUTO_TEST_CASE(btck_script_pubkey) ScriptPubkey script{script_data}; ScriptPubkey script2{script_data_2}; CheckHandle(script, script2); + + BOOST_CHECK_THROW(ScriptPubkey{std::span(static_cast(nullptr), 2)}, std::runtime_error); } BOOST_AUTO_TEST_CASE(btck_transaction_output) @@ -581,6 +588,11 @@ BOOST_AUTO_TEST_CASE(btck_block) CheckHandle(block, block_100); Block block_tx{hex_string_to_byte_vec(REGTEST_BLOCK_DATA[205])}; CheckRange(block_tx.Transactions(), block_tx.CountTransactions()); + auto invalid_data = hex_string_to_byte_vec("012300"); + BOOST_CHECK_THROW(Block{invalid_data}, std::runtime_error); + auto empty_data = hex_string_to_byte_vec(""); + BOOST_CHECK_THROW(Block{empty_data}, std::runtime_error); + BOOST_CHECK_THROW(Block{std::span(static_cast(nullptr), 2)}, std::runtime_error); } Context create_context(std::shared_ptr notifications, ChainType chain_type, std::shared_ptr validation_interface = nullptr) @@ -713,17 +725,6 @@ void chainman_mainnet_validation_test(TestDirectory& test_directory) auto context{create_context(notifications, ChainType::MAINNET, validation_interface)}; auto chainman{create_chainman(test_directory, false, false, false, false, context)}; - { - // Process an invalid block - auto raw_block = hex_string_to_byte_vec("012300"); - BOOST_CHECK_THROW(Block{raw_block}, std::runtime_error); - } - { - // Process an empty block - auto raw_block = hex_string_to_byte_vec(""); - BOOST_CHECK_THROW(Block{raw_block}, std::runtime_error); - } - // mainnet block 1 auto raw_block = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000"); Block block{raw_block};