In commit “test: add IPC submitBlock functional test” (https://github.com/bitcoin/bitcoin/pull/34644/changes/354538db36dc8f04a547b6cbb9159d0a94834901):
I was testing the boundary conditions of the new IPC interface and noticed that sending malformed or truncated block data causes the entire node to crash.
Unlike the standard submitblock RPC which safely catches deserialization errors and returns a -22 Block decode failed error. The IPC implementation seems to let the deserialization exception bubble up, resulting in a SIGABRT (exit code -6).
Since a sv2 client could potentially send bad wire data, this looks like a DoS vector that should be caught at the boundary.
You can reproduce the crash by adding this test to test/functional/interface_ipc_mining.py:
0def run_submit_block_malformed_test(self):
1 """Test that submitting truncated/garbage bytes does not crash the node."""
2 self.log.info("Running submitBlock malformed data test")
3
4 async def async_routine():
5 ctx, mining = await self.make_mining_ctx()
6
7 async with destroying((await mining.createNewBlock(ctx, self.default_block_create_options)).result, ctx) as template:
8 block = await mining_get_block(template, ctx)
9
10 # Truncate the last 15 bytes to create an invalid serialization
11 bad_bytes = block.serialize()[:-15]
12
13 self.log.debug("Submitting truncated block bytes via IPC")
14
15 try:
16 result = await mining.submitBlock(ctx, bad_bytes)
17 assert_equal(result.result, False)
18 except Exception as e:
19 self.log.info(f"IPC exception caught in test: {e}")
20
21 # The node should handle the bad stream gracefully and remain alive
22 assert_equal(self.nodes[0].is_node_stopped(), False)
23
24 asyncio.run(capnp.run(async_routine()))
Running this results in the peer disconnecting and the node crashing:
0AssertionError: [node 0] Node returned unexpected exit code (-6) vs ((0,)) when stopping
1capnp.lib.capnp.KjException: capnp/rpc.c++:2778: disconnected: Peer disconnected.
I suspect the C++ handler unpacking the Data blob into a CBlock needs to be wrapped in a try...catch block to handle the std::ios_base::failure and return a safe rejection reason (e.g., "inconclusive" or a specific decode error), similar to how src/rpc/mining.cpp handles it.