diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index f45b8908ec..4ebc92c02b 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -34,6 +34,7 @@ use blockdata::constants::WITNESS_SCALE_FACTOR; #[cfg(feature="bitcoinconsensus")] use blockdata::script; use blockdata::script::Script; use consensus::{encode, Decodable, Encodable}; +use consensus::encode::MAX_OBJECT_SIZE; use hash_types::{SigHash, Txid, Wtxid}; use VarInt; @@ -566,7 +567,8 @@ impl Encodable for Transaction { } impl Decodable for Transaction { - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode(d: D) -> Result { + let mut d = d.take(MAX_OBJECT_SIZE as u64); let version = i32::consensus_decode(&mut d)?; let input = Vec::::consensus_decode(&mut d)?; // segwit diff --git a/src/consensus/encode.rs b/src/consensus/encode.rs index 54c8366f90..6dd652dd21 100644 --- a/src/consensus/encode.rs +++ b/src/consensus/encode.rs @@ -305,7 +305,7 @@ impl ReadExt for R { } /// Maximum size, in bytes, of a vector we are allowed to decode -pub const MAX_VEC_SIZE: usize = 4_000_000; +pub const MAX_OBJECT_SIZE: usize = 4_000_000; /// Data which can be encoded in a consensus-consistent way pub trait Encodable { @@ -571,10 +571,11 @@ macro_rules! impl_vec { let byte_size = (len as usize) .checked_mul(mem::size_of::<$type>()) .ok_or(self::Error::ParseFailed("Invalid length"))?; - if byte_size > MAX_VEC_SIZE { - return Err(self::Error::OversizedVectorAllocation { requested: byte_size, max: MAX_VEC_SIZE }) + if byte_size > MAX_OBJECT_SIZE { + return Err(self::Error::OversizedVectorAllocation { requested: byte_size, max: MAX_OBJECT_SIZE }) } let mut ret = Vec::with_capacity(len as usize); + let mut d = d.take(MAX_OBJECT_SIZE as u64); for _ in 0..len { ret.push(Decodable::consensus_decode(&mut d)?); } @@ -614,8 +615,8 @@ impl Decodable for Vec { #[inline] fn consensus_decode(mut d: D) -> Result { let len = VarInt::consensus_decode(&mut d)?.0 as usize; - if len > MAX_VEC_SIZE { - return Err(self::Error::OversizedVectorAllocation { requested: len, max: MAX_VEC_SIZE }) + if len > MAX_OBJECT_SIZE { + return Err(self::Error::OversizedVectorAllocation { requested: len, max: MAX_OBJECT_SIZE }) } let mut ret = vec![0u8; len]; d.read_slice(&mut ret)?; @@ -659,10 +660,10 @@ impl Decodable for CheckedData { #[inline] fn consensus_decode(mut d: D) -> Result { let len = u32::consensus_decode(&mut d)?; - if len > MAX_VEC_SIZE as u32 { + if len > MAX_OBJECT_SIZE as u32 { return Err(self::Error::OversizedVectorAllocation { requested: len as usize, - max: MAX_VEC_SIZE + max: MAX_OBJECT_SIZE }); } let checksum = <[u8; 4]>::consensus_decode(&mut d)?; @@ -957,9 +958,9 @@ mod tests { let rand_io_err = Error::Io(io::Error::new(io::ErrorKind::Other, "")); - // Check serialization that `if len > MAX_VEC_SIZE {return err}` isn't inclusive, + // Check serialization that `if len > MAX_OBJECT_SIZE {return err}` isn't inclusive, // by making sure it fails with IO Error and not an `OversizedVectorAllocation` Error. - let err = deserialize::(&serialize(&(super::MAX_VEC_SIZE as u32))).unwrap_err(); + let err = deserialize::(&serialize(&(super::MAX_OBJECT_SIZE as u32))).unwrap_err(); assert_eq!(discriminant(&err), discriminant(&rand_io_err)); test_len_is_max_vec::(); @@ -977,7 +978,7 @@ mod tests { fn test_len_is_max_vec() where Vec: Decodable, T: fmt::Debug { let rand_io_err = Error::Io(io::Error::new(io::ErrorKind::Other, "")); - let varint = VarInt((super::MAX_VEC_SIZE / mem::size_of::()) as u64); + let varint = VarInt((super::MAX_OBJECT_SIZE / mem::size_of::()) as u64); let err = deserialize::>(&serialize(&varint)).unwrap_err(); assert_eq!(discriminant(&err), discriminant(&rand_io_err)); } @@ -997,6 +998,15 @@ mod tests { assert_eq!(cd.ok(), Some(CheckedData(vec![1u8, 2, 3, 4, 5]))); } + #[test] + fn limit_read_test() { + let witness = vec![vec![0u8; 3_999_999]; 2]; + let ser = serialize(&witness); + let mut reader = io::Cursor::new(ser); + let err = Vec::>::consensus_decode(&mut reader); + assert!(err.is_err()); + } + #[test] fn serialization_round_trips() { macro_rules! round_trip { diff --git a/src/internal_macros.rs b/src/internal_macros.rs index dc66ec602a..c34d694e24 100644 --- a/src/internal_macros.rs +++ b/src/internal_macros.rs @@ -33,8 +33,9 @@ macro_rules! impl_consensus_encoding { impl $crate::consensus::Decodable for $thing { #[inline] fn consensus_decode( - mut d: D, + d: D, ) -> Result<$thing, $crate::consensus::encode::Error> { + let mut d = d.take($crate::consensus::encode::MAX_OBJECT_SIZE as u64); Ok($thing { $($field: $crate::consensus::Decodable::consensus_decode(&mut d)?),+ }) diff --git a/src/network/message.rs b/src/network/message.rs index 1ca3a83998..726d09a952 100644 --- a/src/network/message.rs +++ b/src/network/message.rs @@ -29,9 +29,8 @@ use network::address::{Address, AddrV2Message}; use network::message_network; use network::message_blockdata; use network::message_filter; -use consensus::encode::{CheckedData, Decodable, Encodable, VarInt}; +use consensus::encode::{CheckedData, Decodable, Encodable, VarInt, MAX_OBJECT_SIZE}; use consensus::{encode, serialize}; -use consensus::encode::MAX_VEC_SIZE; /// The maximum number of [Inventory] items in an `inv` message. /// @@ -318,8 +317,8 @@ impl Decodable for HeaderDeserializationWrapper { let byte_size = (len as usize) .checked_mul(mem::size_of::()) .ok_or(encode::Error::ParseFailed("Invalid length"))?; - if byte_size > MAX_VEC_SIZE { - return Err(encode::Error::OversizedVectorAllocation { requested: byte_size, max: MAX_VEC_SIZE }) + if byte_size > MAX_OBJECT_SIZE { + return Err(encode::Error::OversizedVectorAllocation { requested: byte_size, max: MAX_OBJECT_SIZE }) } let mut ret = Vec::with_capacity(len as usize); for _ in 0..len { diff --git a/src/util/psbt/map/global.rs b/src/util/psbt/map/global.rs index 7369afa5fb..75ed9e7c63 100644 --- a/src/util/psbt/map/global.rs +++ b/src/util/psbt/map/global.rs @@ -19,6 +19,7 @@ use std::cmp; use blockdata::transaction::Transaction; use consensus::{encode, Encodable, Decodable}; +use consensus::encode::MAX_OBJECT_SIZE; use util::psbt::map::Map; use util::psbt::raw; use util::psbt; @@ -228,8 +229,8 @@ impl Map for Global { impl_psbtmap_consensus_encoding!(Global); impl Decodable for Global { - fn consensus_decode(mut d: D) -> Result { - + fn consensus_decode(d: D) -> Result { + let mut d = d.take(MAX_OBJECT_SIZE as u64); let mut tx: Option = None; let mut version: Option = None; let mut unknowns: BTreeMap> = Default::default(); diff --git a/src/util/psbt/mod.rs b/src/util/psbt/mod.rs index 3cd2ef894c..fa420061ba 100644 --- a/src/util/psbt/mod.rs +++ b/src/util/psbt/mod.rs @@ -21,6 +21,7 @@ use blockdata::script::Script; use blockdata::transaction::Transaction; use consensus::{encode, Encodable, Decodable}; +use consensus::encode::MAX_OBJECT_SIZE; use std::io; @@ -162,7 +163,8 @@ impl Encodable for PartiallySignedTransaction { } impl Decodable for PartiallySignedTransaction { - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode(d: D) -> Result { + let mut d = d.take(MAX_OBJECT_SIZE as u64); let magic: [u8; 4] = Decodable::consensus_decode(&mut d)?; if *b"psbt" != magic { diff --git a/src/util/psbt/raw.rs b/src/util/psbt/raw.rs index 44f80d03ea..e3ed262ce2 100644 --- a/src/util/psbt/raw.rs +++ b/src/util/psbt/raw.rs @@ -19,7 +19,7 @@ use std::{fmt, io}; -use consensus::encode::{self, ReadExt, WriteExt, Decodable, Encodable, VarInt, serialize, deserialize, MAX_VEC_SIZE}; +use consensus::encode::{self, ReadExt, WriteExt, Decodable, Encodable, VarInt, serialize, deserialize, MAX_OBJECT_SIZE}; use hashes::hex; use util::psbt::Error; @@ -81,10 +81,10 @@ impl Decodable for Key { let key_byte_size: u64 = byte_size - 1; - if key_byte_size > MAX_VEC_SIZE as u64 { + if key_byte_size > MAX_OBJECT_SIZE as u64 { return Err(encode::Error::OversizedVectorAllocation { requested: key_byte_size as usize, - max: MAX_VEC_SIZE, + max: MAX_OBJECT_SIZE, }) }