From ef642295c5154cb3b45d5aaa4f7922ffc2e5bbb0 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 24 Aug 2018 00:50:05 +0000 Subject: [PATCH] encodable: reject non-compact VarInts on Vec and Box<[T]> lengths --- src/network/encodable.rs | 76 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/src/network/encodable.rs b/src/network/encodable.rs index 4fa62c56..6551c91a 100644 --- a/src/network/encodable.rs +++ b/src/network/encodable.rs @@ -115,14 +115,36 @@ impl ConsensusDecodable for VarInt { fn consensus_decode(d: &mut D) -> Result { let n = d.read_u8()?; match n { - 0xFF => d.read_u64().map(|n| VarInt(u64::from_le(n))), - 0xFE => d.read_u32().map(|n| VarInt(u32::from_le(n) as u64)), - 0xFD => d.read_u16().map(|n| VarInt(u16::from_le(n) as u64)), + 0xFF => { + let x = d.read_u64()?; + if x < 0x100000000 { + Err(serialize::Error::ParseFailed("non-minimal varint")) + } else { + Ok(VarInt(x)) + } + } + 0xFE => { + let x = d.read_u32()?; + if x < 0x10000 { + Err(serialize::Error::ParseFailed("non-minimal varint")) + } else { + Ok(VarInt(x as u64)) + } + } + 0xFD => { + let x = d.read_u16()?; + if x < 0xFD { + Err(serialize::Error::ParseFailed("non-minimal varint")) + } else { + Ok(VarInt(x as u64)) + } + } n => Ok(VarInt(n as u64)) } } } + // Booleans impl ConsensusEncodable for bool { #[inline] @@ -202,7 +224,7 @@ impl> ConsensusEncodable for Vec> ConsensusDecodable for Vec { #[inline] fn consensus_decode(d: &mut D) -> Result, serialize::Error> { - let VarInt(len): VarInt = ConsensusDecodable::consensus_decode(d)?; + let len = VarInt::consensus_decode(d)?.0; let byte_size = (len as usize) .checked_mul(mem::size_of::()) .ok_or(serialize::Error::ParseFailed("Invalid length"))?; @@ -223,7 +245,7 @@ impl> ConsensusEncodable for Box<[ impl> ConsensusDecodable for Box<[T]> { #[inline] fn consensus_decode(d: &mut D) -> Result, serialize::Error> { - let VarInt(len): VarInt = ConsensusDecodable::consensus_decode(d)?; + let len = VarInt::consensus_decode(d)?.0; let len = len as usize; if len > MAX_VEC_SIZE { return Err(serialize::Error::OversizedVectorAllocation { requested: len, max: MAX_VEC_SIZE }) @@ -366,7 +388,7 @@ impl ConsensusDecodable for HashMap { #[inline] fn consensus_decode(d: &mut D) -> Result, serialize::Error> { - let VarInt(len): VarInt = ConsensusDecodable::consensus_decode(d)?; + let len = VarInt::consensus_decode(d)?.0; let mut ret = HashMap::with_capacity(len as usize); for _ in 0..len { @@ -384,7 +406,7 @@ impl ConsensusDecodable for HashMap mod tests { use super::{CheckedData, VarInt}; - use network::serialize::{deserialize, serialize}; + use network::serialize::{deserialize, serialize, Error}; #[test] fn serialize_int_test() { @@ -435,6 +457,46 @@ mod tests { assert_eq!(serialize(&VarInt(0xF0F0F0F0F0E0)).ok(), Some(vec![0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0])); } + #[test] + fn deserialize_nonminimal_vec() { + match deserialize::>(&[0xfd, 0x00, 0x00]) { + Err(Error::ParseFailed("non-minimal varint")) => {}, + x => panic!(x) + } + match deserialize::>(&[0xfd, 0xfc, 0x00]) { + Err(Error::ParseFailed("non-minimal varint")) => {}, + x => panic!(x) + } + match deserialize::>(&[0xfe, 0xff, 0x00, 0x00, 0x00]) { + Err(Error::ParseFailed("non-minimal varint")) => {}, + x => panic!(x) + } + match deserialize::>(&[0xfe, 0xff, 0xff, 0x00, 0x00]) { + Err(Error::ParseFailed("non-minimal varint")) => {}, + x => panic!(x) + } + match deserialize::>(&[0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) { + Err(Error::ParseFailed("non-minimal varint")) => {}, + x => panic!(x) + } + match deserialize::>(&[0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00]) { + Err(Error::ParseFailed("non-minimal varint")) => {}, + x => panic!(x) + } + + let mut vec_256 = vec![0; 259]; + vec_256[0] = 0xfd; + vec_256[1] = 0x00; + vec_256[2] = 0x01; + assert!(deserialize::>(&vec_256).is_ok()); + + let mut vec_253 = vec![0; 256]; + vec_253[0] = 0xfd; + vec_253[1] = 0xfd; + vec_253[2] = 0x00; + assert!(deserialize::>(&vec_253).is_ok()); + } + #[test] fn serialize_checkeddata_test() { let cd = CheckedData(vec![1u8, 2, 3, 4, 5]);