What changed, and why it matters
This commit adds a new helper function to the PSBT (Partially Signed Bitcoin Transaction) package that checks whether a data reader still has leftover bytes after parsing. Leftover bytes could mean a malformed or malicious PSBT file was not fully processed. The helper lets the library reject such inputs. However, the commit only adds the helper; it does not yet wire it into any actual parsing path, so by itself it does not fix any vulnerability.
Review follow-up commits to see where assertFullyConsumed is invoked and confirm it is applied consistently after PSBT parsing boundaries. If this helper is meant to address a known issue, request or verify a regression test and a changelog/security note.
Security signals we found
New validation helper for trailing/leftover data in a binary parser
Located in PSBT parsing utilities, an area where malformed input handling matters
No caller added in this commit, so defensive effect is not yet active
Evidence from the diff
The change introduces assertFullyConsumed(r io.Reader) in psbt/utils.go. It returns ErrInvalidPsbtFormat if the supplied reader still contains unread bytes. It first tries a Len() method (e.g., bytes.Reader), otherwise reads one byte and expects io.EOF. The function is not called anywhere in the visible diff, so it is preparatory code. It is likely intended to be used after parsing a PSBT section to detect trailing data, which can be relevant to canonical encoding checks and preventing malleability or parser-desync issues.
Changed components
btcd/psbt/utils.goInspect captured patch +25 / −0
diff --git a/psbt/utils.go b/psbt/utils.go
index 2c880e2..9596c48 100644
--- a/psbt/utils.go
+++ b/psbt/utils.go
@@ -278,6 +278,31 @@ func getKey(r io.Reader) (int, []byte, error) {
return int(keyType), keyData, nil
}
+// assertFullyConsumed returns ErrInvalidPsbtFormat if r still has bytes
+// available after parsing.
+func assertFullyConsumed(r io.Reader) error {
+ if lr, ok := r.(interface{ Len() int }); ok {
+ if lr.Len() > 0 {
+ return ErrInvalidPsbtFormat
+ }
+
+ return nil
+ }
+
+ var trailing [1]byte
+ _, err := io.ReadFull(r, trailing[:])
+ switch {
+ case err == nil:
+ return ErrInvalidPsbtFormat
+
+ case errors.Is(err, io.EOF):
+ return nil
+
+ default:
+ return err
+ }
+}
+
// readTxOut is a limited version of wire.ReadTxOut, because the latter is not
// exported.
func readTxOut(txout []byte) (*wire.TxOut, error) {
Why this scored 36/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.