test: verify IPC error handling for invalid coinbase
What changed, and why it matters
This commit only adds a new automated test and a small test helper. It checks that the Bitcoin Core IPC mining server returns a clean error when given invalid data, instead of crashing. There is no change to production code, so it does not introduce or fix a live security vulnerability by itself.
No immediate action required; this is a regression test. If the underlying issue (#33341) has not already been fixed in production code, ensure the node-side IPC deserialization path is hardened separately.
Security signals we found
Test-only change verifying error handling for malformed IPC input
References prior discussion (#33341) about IPC server behavior on invalid coinbase data
No production code patch present in the diff
Evidence from the diff
The diff is test-only: it adds assert_capnp_failed to test_framework/ipc_util.py and uses it in interface_ipc_mining.py to verify that submitSolution with an empty/deserialization-failing payload raises a Cap’n Proto KjException with type FAILED and a SpanReader::read(): end of data: message. An existing block-weight assertion is refactored to use the same helper. No node-side source code is modified.
Changed components
test/functional/interface_ipc_mining.pytest/functional/test_framework/ipc_util.pyInspect captured patch +24 / −10
diff --git a/test/functional/interface_ipc_mining.py b/test/functional/interface_ipc_mining.py
index 6f2237a8..0c3b9cf8 100755
--- a/test/functional/interface_ipc_mining.py
+++ b/test/functional/interface_ipc_mining.py
@@ -7,7 +7,6 @@ import asyncio
import time
from contextlib import AsyncExitStack
from io import BytesIO
-import platform
from test_framework.blocktools import NULL_OUTPOINT
from test_framework.messages import (
MAX_BLOCK_WEIGHT,
@@ -42,6 +41,7 @@ from test_framework.ipc_util import (
mining_wait_next_template,
wait_and_do,
make_mining_ctx,
+ assert_capnp_failed
)
# Test may be skipped and not have capnp installed
@@ -325,15 +325,7 @@ class IPCMiningTest(BitcoinTestFramework):
await mining.createNewBlock(ctx, opts)
raise AssertionError("createNewBlock unexpectedly succeeded")
except capnp.lib.capnp.KjException as e:
- if e.description == "remote exception: unknown non-KJ exception of type: kj::Exception":
- # macOS + REDUCE_EXPORTS bug: Cap'n Proto fails to recognize
- # its own exception type and returns a generic error instead.
- # https://github.com/bitcoin/bitcoin/pull/34422#discussion_r2863852691
- # Assert this only occurs on Darwin until fixed.
- assert_equal(platform.system(), "Darwin")
- else:
- assert_equal(e.description, "remote exception: std::exception: block_reserved_weight (0) must be at least 2000 weight units")
- assert_equal(e.type, "FAILED")
+ assert_capnp_failed(e, "remote exception: std::exception: block_reserved_weight (0) must be at least 2000 weight units")
asyncio.run(capnp.run(async_routine()))
@@ -357,6 +349,13 @@ class IPCMiningTest(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
original_version = block.nVersion
+ self.log.debug("Submit solution that can't be deserialized")
+ try:
+ await template.submitSolution(ctx, 0, 0, 0, b"")
+ raise AssertionError("submitSolution unexpectedly succeeded")
+ except capnp.lib.capnp.KjException as e:
+ assert_capnp_failed(e, "remote exception: std::exception: SpanReader::read(): end of data:")
+
self.log.debug("Submit a block with a bad version")
block.nVersion = 0
block.solve()
diff --git a/test/functional/test_framework/ipc_util.py b/test/functional/test_framework/ipc_util.py
index c80f78f7..03e3b5d5 100644
--- a/test/functional/test_framework/ipc_util.py
+++ b/test/functional/test_framework/ipc_util.py
@@ -10,9 +10,13 @@ from dataclasses import dataclass
from io import BytesIO
from pathlib import Path
import shutil
+import platform
from typing import Optional
from test_framework.messages import CBlock
+from test_framework.util import (
+ assert_equal
+)
# Test may be skipped and not have capnp installed
try:
@@ -155,3 +159,14 @@ async def make_mining_ctx(self):
self.log.debug("Create Mining proxy object")
mining = init.makeMining(ctx).result
return ctx, mining
+
+def assert_capnp_failed(e, description_prefix):
+ if e.description == "remote exception: unknown non-KJ exception of type: kj::Exception":
+ # macOS + REDUCE_EXPORTS bug: Cap'n Proto fails to recognize
+ # its own exception type and returns a generic error instead.
+ # https://github.com/bitcoin/bitcoin/pull/34422#discussion_r2863852691
+ # Assert this only occurs on Darwin until fixed.
+ assert_equal(platform.system(), "Darwin")
+ else:
+ assert e.description.startswith(description_prefix), f"Expected description starting with '{description_prefix}', got '{e.description}'"
+ assert_equal(e.type, "FAILED")
Why this scored 12/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.