kernel: assert invalid buffer preconditions in `btck_*_create` functions
What changed, and why it matters
This commit changes several Bitcoin Core kernel library functions so they crash the program (via assertions) instead of returning a null pointer when callers pass a null buffer with a non-zero length. It also tightens one function to require exactly 80-byte block headers. The change treats these as programmer errors rather than runtime failures, which can help catch misuse early but may cause denial-of-service crashes if an attacker can trigger those calls with bad inputs.
Review all callers of the affected libbitcoinkernel functions to ensure they never pass null buffers with non-zero lengths, especially any callers that process network or user-supplied data. Consider whether assertions are appropriate for library boundaries or whether input validation with an error return is safer. Re-add tests that verify the new precondition behavior in release builds where assertions may be disabled.
Security signals we found
API contract hardened from graceful null return to assertion/abort on invalid buffer preconditions
Potential denial-of-service vector introduced if untrusted input can reach these kernel functions with null+non-zero-length arguments
Block header length now strictly enforced as exactly 80 bytes via assertion
Test coverage for invalid block header inputs removed because behavior changed to abort
Evidence from the diff
The patch converts defensive nullptr+len checks in btck_transaction_create, btck_script_pubkey_create, btck_block_create, btck_chainstate_manager_options_create, and btck_block_header_create from returning nullptr into assert() statements. For btck_block_header_create it additionally asserts raw_block_header_len == 80. The header is updated to mark the block header argument non-null. Tests for invalid-length block headers are removed because they now abort in debug builds. This is a hardening change that shifts the API contract: invalid caller preconditions now terminate the process rather than return an error code.
Changed components
src/kernel/bitcoinkernel.cppsrc/kernel/bitcoinkernel.hsrc/test/kernel/test_kernel.cpplibbitcoinkernel C APIInspect captured patch +13 / −24
diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp
index aa095cab..3d0eef77 100644
--- a/src/kernel/bitcoinkernel.cpp
+++ b/src/kernel/bitcoinkernel.cpp
@@ -506,9 +506,7 @@ struct btck_ConsensusParams: Handle<btck_ConsensusParams, Consensus::Params> {};
btck_Transaction* btck_transaction_create(const void* raw_transaction, size_t raw_transaction_len)
{
- if (raw_transaction == nullptr && raw_transaction_len != 0) {
- return nullptr;
- }
+ assert(raw_transaction != nullptr || raw_transaction_len == 0);
try {
SpanReader stream{std::span{reinterpret_cast<const std::byte*>(raw_transaction), raw_transaction_len}};
return btck_Transaction::create(std::make_shared<const CTransaction>(deserialize, TX_WITH_WITNESS, stream));
@@ -573,9 +571,7 @@ 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;
- }
+ assert(script_pubkey != nullptr || script_pubkey_len == 0);
auto data = std::span{reinterpret_cast<const uint8_t*>(script_pubkey), script_pubkey_len};
return btck_ScriptPubkey::create(data.begin(), data.end());
}
@@ -965,7 +961,9 @@ btck_BlockValidationResult btck_block_validation_state_get_block_validation_resu
btck_ChainstateManagerOptions* btck_chainstate_manager_options_create(const btck_Context* context, const char* data_dir, size_t data_dir_len, const char* blocks_dir, size_t blocks_dir_len)
{
- if (data_dir == nullptr || data_dir_len == 0 || blocks_dir == nullptr || blocks_dir_len == 0) {
+ assert(data_dir != nullptr || data_dir_len == 0);
+ assert(blocks_dir != nullptr || blocks_dir_len == 0);
+ if (data_dir_len == 0 || blocks_dir_len == 0) {
LogError("Failed to create chainstate manager options: dir must be non-null and non-empty");
return nullptr;
}
@@ -1116,9 +1114,7 @@ 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;
- }
+ assert(raw_block != nullptr || raw_block_length == 0);
auto block{std::make_shared<CBlock>()};
SpanReader stream{std::span{reinterpret_cast<const std::byte*>(raw_block), raw_block_length}};
@@ -1381,9 +1377,7 @@ int btck_chain_contains(const btck_Chain* chain, const btck_BlockTreeEntry* entr
btck_BlockHeader* btck_block_header_create(const void* raw_block_header, size_t raw_block_header_len)
{
- if (raw_block_header == nullptr && raw_block_header_len != 0) {
- return nullptr;
- }
+ assert(raw_block_header != nullptr && raw_block_header_len == 80);
auto header{std::make_unique<CBlockHeader>()};
SpanReader stream{std::span{reinterpret_cast<const std::byte*>(raw_block_header), raw_block_header_len}};
diff --git a/src/kernel/bitcoinkernel.h b/src/kernel/bitcoinkernel.h
index 577c4c0a..8afef786 100644
--- a/src/kernel/bitcoinkernel.h
+++ b/src/kernel/bitcoinkernel.h
@@ -1144,12 +1144,11 @@ BITCOINKERNEL_API const btck_BlockTreeEntry* BITCOINKERNEL_WARN_UNUSED_RESULT bt
*
* @param[in] context Non-null, the created options and through it the chainstate manager will
* associate with this kernel context for the duration of their lifetimes.
- * @param[in] data_directory Non-null, non-empty path string of the directory containing the
- * chainstate data. If the directory does not exist yet, it will be
- * created.
- * @param[in] blocks_directory Non-null, non-empty path string of the directory containing the block
- * data. If the directory does not exist yet, it will be created.
- * @return The allocated chainstate manager options, or null on error.
+ * @param[in] data_directory Path string of the directory containing the chainstate data. If the directory
+ * does not exist yet, it will be created.
+ * @param[in] blocks_directory Path string of the directory containing the block data. If the directory
+ * does not exist yet, it will be created.
+ * @return The allocated chainstate manager options, or null on error (e.g. if a path is invalid).
*/
BITCOINKERNEL_API btck_ChainstateManagerOptions* BITCOINKERNEL_WARN_UNUSED_RESULT btck_chainstate_manager_options_create(
const btck_Context* context,
@@ -1869,7 +1868,7 @@ BITCOINKERNEL_API void btck_block_hash_destroy(btck_BlockHash* block_hash);
* @return btck_BlockHeader, or null on error.
*/
BITCOINKERNEL_API btck_BlockHeader* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_create(
- const void* raw_block_header, size_t raw_block_header_len);
+ const void* raw_block_header, size_t raw_block_header_len) BITCOINKERNEL_ARG_NONNULL(1);
/**
* @brief Copy a btck_BlockHeader.
diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp
index 0489839d..ca0e7cd6 100644
--- a/src/test/kernel/test_kernel.cpp
+++ b/src/test/kernel/test_kernel.cpp
@@ -683,10 +683,6 @@ BOOST_AUTO_TEST_CASE(btck_block_header_tests)
BlockHeader header_1{hex_string_to_byte_vec("00c00020e7cb7b4de21d26d55bd384017b8bb9333ac3b2b55bed00000000000000000000d91b4484f801b99f03d36b9d26cfa83420b67f81da12d7e6c1e7f364e743c5ba9946e268b4dd011799c8533d")};
CheckHandle(header_0, header_1);
- // Test error handling for invalid data
- BOOST_CHECK_THROW(BlockHeader{hex_string_to_byte_vec("00")}, std::runtime_error);
- BOOST_CHECK_THROW(BlockHeader{hex_string_to_byte_vec("")}, std::runtime_error);
-
// Test all header field accessors using mainnet block 1
auto mainnet_block_1_header = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299");
BlockHeader header{mainnet_block_1_header};
Why this scored 28/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.