fix(legacy/netsync): bounds-check parent output index in ExtendTransaction#768
Conversation
|
🤖 Claude Code Review Status: Complete This PR adds critical bounds checking for parent output indices in No issues found. The implementation:
|
…ction (refs #4564)
143dda4 to
799533e
Compare
Benchmark Comparison ReportBaseline: Current: Summary
All benchmark results (sec/op)
Threshold: >10% with p < 0.05 | Generated: 2026-05-13 14:07 UTC |
Move the OOB bounds check ahead of the 120s WaitForParent loop in ExtendTransaction. Parent Outputs are populated at wire-parse time and never mutated afterwards, so the check is safe to run before waiting and lets a malformed peer block be rejected without burning up to two minutes per offending input on the polling loop. Refactor the test fixture into a buildOOBFixture helper and add TestSyncManager_extendFromTxMap_OOB so the sibling phase-1 path's TxInvalidError contract is now covered by a regression test in its own right. Loosen the string assertions in the existing test to errors.Is plus the parent hash so benign error-message rewording no longer breaks the test. Refs #4564
|
oskarszoon
left a comment
There was a problem hiding this comment.
Approve. Fix is correct, both regression tests trigger the original panic when the bounds check is mentally removed, error type harmonised to TxInvalidError on both paths, the "Outputs populated at wire-parse and never mutated" justification for placing the check before WaitForParent checks out (grep for mutation on the netsync path returns nothing).
Two things worth flagging:
- Same DoS shape exists unfixed in
services/blockvalidation/quick_validate.goat:1061-1062and:1399-1400— unguardedparentTx.Outputs[input.PreviousTxOutIndex]against peer-controlledparentTxlookups. Same panic shape as #4564, but in the block-validation hot path (so worse blast radius). Out of scope for this PR; worth filing as a separate issue / follow-up PR. - For the PR description / commit log: worth noting that
ExtendTransaction(the parallel path getting the new check) has no production callers in the current tree — only the phase-1 pathextendFromTxMapis reachable from peer blocks today, and that path already had the bounds check pre-PR. So the practical runtime impact of #768 is the error-class harmonisation at line 1011; the new check at line 1256 is forward-looking defense-in-depth for when the parallel path gets wired up. Doesn't change the fix or the urgency, but clarifies what's actually closing the live exposure vs what's pre-emptive.



Closes #4564.
Summary
ExtendTransactiondereferencesprevTxWrapper.Tx.Outputs[input.PreviousTxOutIndex]without a bounds check. A peer can send a block where a child tx references its in-block parent withPreviousTxOutIndex >= len(parent.Outputs), panicking the node before full block validation runs. The phase-1 path (extendFromTxMap, around line 1011) already had this guard; the parallel path inExtendTransaction(lines ~1272-1273) was missed.Fix
Insert the same shape of bounds check that exists in
extendFromTxMap, immediately before the dereference. On out-of-range, returnerrors.NewTxInvalidError(...)with the full context (offending tx, input index, parent, output count). The block came from a peer; the transaction is invalid, soTxInvalidErroris more semantically correct thanProcessingError.Test plan
TestSyncManager_ExtendTransaction_OOBconstructs a parent with 2 outputs and a child whose input hasPreviousTxOutIndex == 5; pre-fix panics withindex out of range [5] with length 2; post-fix returnsTxInvalidErrorwith the expected message.go test -race ./services/legacy/netsync/...passes.