psbt: Handle underflow on small key byte length
What changed, and why it matters
This commit fixes a bug in how the library reads PSBT (Partially Signed Bitcoin Transaction) keys. A recent change accidentally made it possible for a malformed input to cause an arithmetic underflow when calculating how many bytes of key data remain. In debug builds that would panic (crash); in release builds it could wrap around and behave incorrectly. The fix uses a safe checked subtraction and returns a proper parsing error instead.
Apply the patch. It is a minimal, defensive fix. If running an affected version, avoid parsing untrusted PSBT data until patched, or ensure release builds are used where the impact is reduced to incorrect behavior rather than panic.
Security signals we found
Integer underflow in length calculation
Debug-build panic / potential release-build wraparound
Malformed input can trigger abnormal termination
PSBT parsing error path now returns InvalidKey instead of panicking
Evidence from the diff
In bitcoin/src/psbt/raw.rs, the key data length is computed as byte_size - type_size, where type_size is the encoded length of the key type’s CompactSize. If a malformed PSBT key has a total byte_size smaller than type_size, the subtraction underflows. On debug builds this panics; on release builds it wraps (u64) and may lead to oversized allocation checks or other misbehavior. The patch replaces the subtraction with checked_sub and returns Error::InvalidKey when an underflow would occur.
Changed components
bitcoin/src/psbt/raw.rsPSBT key deserialization (Key::decode / read)Inspect captured patch +9 / −1
diff --git a/bitcoin/src/psbt/raw.rs b/bitcoin/src/psbt/raw.rs
index 00e40715..d719556f 100644
--- a/bitcoin/src/psbt/raw.rs
+++ b/bitcoin/src/psbt/raw.rs
@@ -87,7 +87,15 @@ impl Key {
0x10000..=0xFFFF_FFFF => 5,
_ => 9,
};
- let key_byte_size = byte_size - type_size;
+
+ // This may cause an underflow/panic if the byte count is insufficient to even capture
+ // the key type CompactSize value. So we use a checked_sub here.
+ let key_byte_size = match byte_size.checked_sub(type_size) {
+ Some(val) => val,
+ None => {
+ return Err(Error::InvalidKey(Self { type_value, key_data: vec![] }));
+ }
+ };
if key_byte_size > MAX_VEC_SIZE {
return Err(encode::Error::Parse(encode::ParseError::OversizedVectorAllocation {
Why this scored 65/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.