@@ -167,7 +167,7 @@ pub fn deserialize<T: Decodable>(data: &[u8]) -> Result<T, Error> {
167167/// doesn't consume the entire vector.
168168pub fn deserialize_partial < T : Decodable > ( data : & [ u8 ] ) -> Result < ( T , usize ) , Error > {
169169 let mut decoder = Cursor :: new ( data) ;
170- let rv = Decodable :: consensus_decode ( & mut decoder) ?;
170+ let rv = Decodable :: consensus_decode_from_finite_reader ( & mut decoder) ?;
171171 let consumed = decoder. position ( ) as usize ;
172172
173173 Ok ( ( rv, consumed) )
@@ -319,6 +319,47 @@ pub trait Encodable {
319319
320320/// Data which can be encoded in a consensus-consistent way
321321pub trait Decodable : Sized {
322+ /// Decode `Self` from a size-limited reader.
323+ ///
324+ /// Like `consensus_decode` but relies on the reader being
325+ /// limited in the amount of data it returns, e.g. by
326+ /// being wrapped in [`std::io::Take`].
327+ ///
328+ /// Failling to obide to this requirement might lead to
329+ /// memory exhaustion caused by malicious inputs.
330+ ///
331+ /// Users should default to `consensus_decode`, but
332+ /// when data to be decoded is already in a byte vector
333+ /// of a limited size, calling this function directly
334+ /// might be marginally faster (due to avoiding
335+ /// extra checks).
336+ ///
337+ /// ### Rules for trait implementations
338+ ///
339+ /// * Simple types that that have a fixed size (own and member fields),
340+ /// don't have to overwrite this method, or be concern with it.
341+ /// * Types that deserialize using externally provided length
342+ /// should implement it:
343+ /// * Make `consensus_decode` forward to `consensus_decode_bytes_from_finite_reader`
344+ /// with the reader wrapped by `Take`. Failure to do so, without other
345+ /// forms of memory exhaustion protection might lead to resource exhaustion
346+ /// vulnerability.
347+ /// * Put a max cap on things like `Vec::with_capacity` to avoid oversized
348+ /// allocations, and rely on the reader running out of data, and collections
349+ /// reallocating on a legitimately oversized input data, instead of trying
350+ /// to enforce arbitrary length limits.
351+ /// * Types that contain other types that implement custom `consensus_decode_from_finite_reader`,
352+ /// should also implement it applying same rules, and in addition make sure to call
353+ /// `consensus_decode_from_finite_reader` on all members, to avoid creating redundant
354+ /// `Take` wrappers. Failure to do so might result only in a tiny performance hit.
355+ fn consensus_decode_from_finite_reader < D : io:: Read > ( d : D ) -> Result < Self , Error > {
356+ // This method is always strictly less general than, `consensus_decode`,
357+ // so it's safe and make sense to default to just calling it.
358+ // This way most types, that don't care about protecting against
359+ // resource exhaustion due to malicious input, can just ignore it.
360+ Self :: consensus_decode ( d)
361+ }
362+
322363 /// Decode an object with a well-defined format
323364 fn consensus_decode < D : io:: Read > ( d : D ) -> Result < Self , Error > ;
324365}
@@ -555,23 +596,29 @@ macro_rules! impl_vec {
555596 Ok ( len)
556597 }
557598 }
599+
558600 impl Decodable for Vec <$type> {
559601 #[ inline]
560- fn consensus_decode <D : io:: Read >( mut d: D ) -> Result <Self , Error > {
561- let len = VarInt :: consensus_decode ( & mut d) ?. 0 ;
562- let byte_size = ( len as usize )
563- . checked_mul ( mem :: size_of :: <$type> ( ) )
564- . ok_or ( self :: Error :: ParseFailed ( "Invalid length" ) ) ? ;
565- if byte_size > MAX_VEC_SIZE {
566- return Err ( self :: Error :: OversizedVectorAllocation { requested : byte_size , max : MAX_VEC_SIZE } )
567- }
568- let mut ret = Vec :: with_capacity ( len as usize ) ;
569- let mut d = d . take ( MAX_VEC_SIZE as u64 ) ;
602+ fn consensus_decode_from_finite_reader <D : io:: Read >( mut d: D ) -> Result <Self , Error > {
603+ let len = VarInt :: consensus_decode_from_finite_reader ( & mut d) ?. 0 ;
604+ // Do not allocate upfront more items than if the sequnce of type
605+ // occupied roughly quarter a block. This should never be the case
606+ // for normal data, but even if that's not true - `push` will just
607+ // reallocate.
608+ // Note: OOM protection relies on reader eventually running out of
609+ // data to feed us.
610+ let max_capacity = MAX_VEC_SIZE / 4 / mem :: size_of :: <$type> ( ) ;
611+ let mut ret = Vec :: with_capacity ( core :: cmp :: min ( len as usize , max_capacity ) ) ;
570612 for _ in 0 ..len {
571- ret. push( Decodable :: consensus_decode ( & mut d) ?) ;
613+ ret. push( Decodable :: consensus_decode_from_finite_reader ( & mut d) ?) ;
572614 }
573615 Ok ( ret)
574616 }
617+
618+ #[ inline]
619+ fn consensus_decode<D : io:: Read >( d: D ) -> Result <Self , Error > {
620+ Self :: consensus_decode_from_finite_reader( d. take( MAX_VEC_SIZE as u64 ) )
621+ }
575622 }
576623 }
577624}
@@ -597,6 +644,33 @@ pub(crate) fn consensus_encode_with_size<S: io::Write>(data: &[u8], mut s: S) ->
597644}
598645
599646
647+ struct ReadBytesFromFiniteReaderOpts {
648+ len : usize ,
649+ chunk_size : usize ,
650+ }
651+
652+ /// Read `opts.len` bytes from reader, where `opts.len` could potentially be malicious
653+ ///
654+ /// This function relies on reader being bound in amount of data
655+ /// it returns for OOM protection. See [`Decodable::consensus_decode_from_finite_reader`].
656+ #[ inline]
657+ fn read_bytes_from_finite_reader < D : io:: Read > ( mut d : D , mut opts : ReadBytesFromFiniteReaderOpts ) -> Result < Vec < u8 > , Error > {
658+ let mut ret = vec ! [ ] ;
659+
660+ assert_ne ! ( opts. chunk_size, 0 ) ;
661+
662+ while opts. len > 0 {
663+ let chunk_start = ret. len ( ) ;
664+ let chunk_size = core:: cmp:: min ( opts. len , opts. chunk_size ) ;
665+ let chunk_end = chunk_start + chunk_size;
666+ ret. resize ( chunk_end, 0u8 ) ;
667+ d. read_slice ( & mut ret[ chunk_start..chunk_end] ) ?;
668+ opts. len -= chunk_size;
669+ }
670+
671+ Ok ( ret)
672+ }
673+
600674impl Encodable for Vec < u8 > {
601675 #[ inline]
602676 fn consensus_encode < S : io:: Write > ( & self , s : S ) -> Result < usize , io:: Error > {
@@ -606,14 +680,15 @@ impl Encodable for Vec<u8> {
606680
607681impl Decodable for Vec < u8 > {
608682 #[ inline]
609- fn consensus_decode < D : io:: Read > ( mut d : D ) -> Result < Self , Error > {
683+ fn consensus_decode_from_finite_reader < D : io:: Read > ( mut d : D ) -> Result < Self , Error > {
610684 let len = VarInt :: consensus_decode ( & mut d) ?. 0 as usize ;
611- if len > MAX_VEC_SIZE {
612- return Err ( self :: Error :: OversizedVectorAllocation { requested : len, max : MAX_VEC_SIZE } )
613- }
614- let mut ret = vec ! [ 0u8 ; len] ;
615- d. read_slice ( & mut ret) ?;
616- Ok ( ret)
685+ // most real-world vec of bytes data, wouldn't be larger than 128KiB
686+ read_bytes_from_finite_reader ( d, ReadBytesFromFiniteReaderOpts { len, chunk_size : 128 * 1024 } )
687+ }
688+
689+ #[ inline]
690+ fn consensus_decode < D : io:: Read > ( d : D ) -> Result < Self , Error > {
691+ Self :: consensus_decode_from_finite_reader ( d. take ( MAX_VEC_SIZE as u64 ) )
617692 }
618693}
619694
@@ -625,9 +700,14 @@ impl Encodable for Box<[u8]> {
625700}
626701
627702impl Decodable for Box < [ u8 ] > {
703+ #[ inline]
704+ fn consensus_decode_from_finite_reader < D : io:: Read > ( d : D ) -> Result < Self , Error > {
705+ <Vec < u8 > >:: consensus_decode_from_finite_reader ( d) . map ( From :: from)
706+ }
707+
628708 #[ inline]
629709 fn consensus_decode < D : io:: Read > ( d : D ) -> Result < Self , Error > {
630- < Vec < u8 > > :: consensus_decode ( d ) . map ( From :: from )
710+ Self :: consensus_decode_from_finite_reader ( d . take ( MAX_VEC_SIZE as u64 ) )
631711 }
632712}
633713
@@ -651,17 +731,11 @@ impl Encodable for CheckedData {
651731
652732impl Decodable for CheckedData {
653733 #[ inline]
654- fn consensus_decode < D : io:: Read > ( mut d : D ) -> Result < Self , Error > {
655- let len = u32:: consensus_decode ( & mut d) ?;
656- if len > MAX_VEC_SIZE as u32 {
657- return Err ( self :: Error :: OversizedVectorAllocation {
658- requested : len as usize ,
659- max : MAX_VEC_SIZE
660- } ) ;
661- }
662- let checksum = <[ u8 ; 4 ] >:: consensus_decode ( & mut d) ?;
663- let mut ret = vec ! [ 0u8 ; len as usize ] ;
664- d. read_slice ( & mut ret) ?;
734+ fn consensus_decode_from_finite_reader < D : io:: Read > ( mut d : D ) -> Result < Self , Error > {
735+ let len = u32:: consensus_decode_from_finite_reader ( & mut d) ? as usize ;
736+
737+ let checksum = <[ u8 ; 4 ] >:: consensus_decode_from_finite_reader ( & mut d) ?;
738+ let ret = read_bytes_from_finite_reader ( d, ReadBytesFromFiniteReaderOpts { len, chunk_size : MAX_VEC_SIZE } ) ?;
665739 let expected_checksum = sha2_checksum ( & ret) ;
666740 if expected_checksum != checksum {
667741 Err ( self :: Error :: InvalidChecksum {
@@ -672,6 +746,10 @@ impl Decodable for CheckedData {
672746 Ok ( CheckedData ( ret) )
673747 }
674748 }
749+
750+ fn consensus_decode < D : io:: Read > ( d : D ) -> Result < Self , Error > {
751+ Self :: consensus_decode_from_finite_reader ( d. take ( MAX_VEC_SIZE as u64 ) )
752+ }
675753}
676754
677755// References
@@ -1064,5 +1142,21 @@ mod tests {
10641142
10651143 }
10661144 }
1145+
1146+ #[ test]
1147+ fn test_read_bytes_from_finite_reader ( ) {
1148+ let data : Vec < u8 > = ( 0 ..10 ) . collect ( ) ;
1149+
1150+ for chunk_size in 1 ..20 {
1151+ assert_eq ! (
1152+ read_bytes_from_finite_reader(
1153+ io:: Cursor :: new( & data) ,
1154+ ReadBytesFromFiniteReaderOpts { len: data. len( ) , chunk_size }
1155+ ) . unwrap( ) ,
1156+ data
1157+ ) ;
1158+ }
1159+ }
1160+
10671161}
10681162
0 commit comments