Harden block parsing against malformed backend responses#5
Merged
Conversation
A buggy or malicious backend (the RPC connection is plain HTTP) could previously crash lightwalletd via several panic paths, reachable from the block ingestor and from client-driven GetBlock/GetBlockRange cache misses: - getBlockFromRPC indexed the verbose getblock reply's txid list once per parsed transaction; a raw block with more transactions than the verbose reply listed panicked with index out of range. - Block.GetHeight() indexed vtx[0].transparentInputs[0] unguarded, but ParseFromSlice accepts a block with zero transactions, and a first transaction with no transparent inputs parses fine (the parser does not verify it is a coinbase). - hash32.FromSlice and ReverseSlice documented zero-padding short input but used a slice-to-array conversion, which panics on input shorter than 32 bytes. (All current callers pass 32 bytes; this was a latent footgun contradicting its documentation.) Also: - A malformed (non-JSON-shaped) verbose getblock response killed the whole process via Log.Fatal; return an error instead so the caller can retry or report it. - ParseFromSlice pre-allocated the transaction slice from the untrusted tx_count varint (up to 0x02000000), allowing ~268MB allocations per crafted block; cap the capacity hint by the remaining data length. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the panic/crash vectors found while auditing the block-fetch codepaths for the zcash#565 backport (#3). All are pre-existing on master, independent of #3, and reachable from both the block ingestor and client-driven
GetBlock/GetBlockRangecache misses whenever the backend (or a MITM — the RPC connection is plain HTTP) serves inconsistent or malformed responses.Fixes
getBlockFromRPCtxid-list indexing panic — the verbose reply's txid list was indexed once per parsed transaction; a raw block with more transactions than the verbose reply listed panicked. Now returns an error. (This is the panic class we hit while writing the parallel-ingestor tests: testdata block 380643 has 2 transactions.)Block.GetHeight()unguarded indexing —ParseFromSliceaccepts a block with zero transactions, and a first transaction with no transparent inputs parses fine (the parser doesn't verify it's a coinbase);vtx[0].transparentInputs[0]panicked on either. Now returns -1, which existing callers already handle.hash32.FromSlice/ReverseSlicedoc/behavior mismatch — documented as zero-padding short input, but the slice→array conversion panics on len < 32. Now matches the docs. All current callers pass exactly 32 bytes, so this is a latent footgun fix, not a behavior change.Log.Fatalon malformed verbose getblock JSON — a non-JSON-shaped (but successful) response killed the whole process; now returns an error so callers retry/report.ParseFromSlicepre-allocated the tx slice from the untrustedtx_countvarint (cap 0x02000000 → ~268MB per crafted block, × concurrent fetchers); the capacity hint is now bounded by the remaining data length.Testing
Regression tests for 1, 2, 4 (
TestGetBlockFromRPCBadBackendReplies,TestGetHeightMalformedBlocks) and 3 (TestFromSlice,TestReverseSliceShort). All verified to panic (1–3) or exit (4) when run against master's unguarded code, and pass with the guards. Full suite passes under-race.No conflicts with #3: same files in places, but disjoint hunks; the two PRs merge cleanly in either order.
These fixes are also candidates for upstreaming to zcash/lightwalletd.
🤖 Generated with Claude Code