Bug 92

Chain.getBlockHash with a height above the tip aborts the node

Status
Open, fix under review
Symptom
Assertion failure; worst effect seen: assertion failure
Where seen
Branch-build
Trigger
Unusual input from a peer or caller
First reported
2026-09-09 (first report)
Fixed
Not yet
Fix
bitcoin#36520
In released binaries
No
Defect ever on master
Yes

Summary

Calling getBlockHash on the Chain IPC interface with a height that is not in the active chain, for example one above the current tip, makes bitcoin-node abort on an Assert. The method was written for the wallet running in the same process, which only asks for heights it knows exist; over IPC the height comes from another program. Only the open PR bitcoin#29409, which exposes Chain over IPC, makes this reachable, so master and releases are not affected. The fix is in bitcoin#36520, not yet merged.

What happened

Root cause

ChainImpl::getBlockHash() in src/node/interfaces.cpp looks the height up in the active chain and wraps the result in Assert(...), which aborts the process if the lookup returns null. The interface comment states the precondition plainly: "Height must be valid or this function will abort." That is acceptable for in-process callers, but over IPC the height is input from another process. Any client can send an out-of-range value, and even a careful client that reads getHeight() first can lose a race with a reorganization that shortens the chain.

Fix

No fix is merged yet. In bitcoin#36520 ("interfaces: Avoid node crashes from invalid Chain IPC calls"), the commit 42fcc504e21b, by ryanofsky and written with Claude, makes getBlockHash throw std::out_of_range instead. libmultiprocess returns the exception to the IPC client as an error, and the node keeps running. A new invalidArguments test in src/test/interfaces_tests.cpp checks heights one above the tip and -1. The same commit also fixes null transaction arguments.

Next steps

Timeline

Lessons

What would have prevented this bug, found it sooner, or made it easier to debug. The patterns page groups these lessons across all bugs.

Pattern An in-process interface trusting what now comes from another process: An assertion on what is now input from a peer

  • Prevented by a different API shape
  • Earliest detector a test with a non-C++ client (did not exist then)
  • Diagnosis easy
  • Fix design easy
  • Debugging aids a clear assertion message
  • Rust partly

Would Rust have prevented it? Partly. Rust would make the lookup return an Option and an unwrap would panic just like the Assert; the question is whether the caller is trusted, which no type system answers, though Option makes the missing case visible at the call site. More on Rust

Could it have been prevented?

Yes, by treating every method exposed over IPC as taking untrusted input. The Chain::getBlockHash comment already said "Height must be valid or this function will abort", which is a reasonable contract for the in-process wallet and a remote crash for any other caller. The fix in bitcoin#36520 shows the rule: report an invalid argument as an error (here std::out_of_range, which libmultiprocess returns to the client) rather than asserting. Doing that sweep before bitcoin#29409 exposes the interface is cheaper than finding each case by hand.

Could it have been found sooner?

Yes, by a test that calls each newly exposed method with out-of-range values, or by a fuzz target over the IPC interface. Neither existed for Chain; the existing tests use the interface the way the wallet does. It was found when a reviewer, zaidmstrr, called the method by hand with a height above the tip.

What made it hard to debug?

Nothing: the assertion message names the file, function and condition.

Lesson

When an internal interface becomes a remote one, its documented preconditions become input validation that someone has to write: assertions that protected against programming errors now let any client abort the server.

Classification

The survey's classification of this bug. The explorer charts these fields across all bugs; the method page explains them.

Field Value
Symptom assert
Worst effect observed assert
Seen in branch-build
Scope production
Trigger Unusual input (unusual-input)
Phase steady-state
Side server
Component bitcoin-ipc
Root cause kind assumption-violated
Fix kind local-fix
Detection manual-test
Needs a peer that is not libmultiprocess C++ no
Needed a sanitizer to see no
Defect ever on master yes
Released no
Confidence high