@@ -167,7 +167,7 @@ pub fn deserialize<T: Decodable>(data: &[u8]) -> Result<T, Error> {
167
167
/// doesn't consume the entire vector.
168
168
pub fn deserialize_partial < T : Decodable > ( data : & [ u8 ] ) -> Result < ( T , usize ) , Error > {
169
169
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) ?;
171
171
let consumed = decoder. position ( ) as usize ;
172
172
173
173
Ok ( ( rv, consumed) )
@@ -319,6 +319,47 @@ pub trait Encodable {
319
319
320
320
/// Data which can be encoded in a consensus-consistent way
321
321
pub 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
+
322
363
/// Decode an object with a well-defined format
323
364
fn consensus_decode < D : io:: Read > ( d : D ) -> Result < Self , Error > ;
324
365
}
@@ -555,23 +596,29 @@ macro_rules! impl_vec {
555
596
Ok ( len)
556
597
}
557
598
}
599
+
558
600
impl Decodable for Vec <$type> {
559
601
#[ 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 ) ) ;
570
612
for _ in 0 ..len {
571
- ret. push( Decodable :: consensus_decode ( & mut d) ?) ;
613
+ ret. push( Decodable :: consensus_decode_from_finite_reader ( & mut d) ?) ;
572
614
}
573
615
Ok ( ret)
574
616
}
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
+ }
575
622
}
576
623
}
577
624
}
@@ -597,6 +644,33 @@ pub(crate) fn consensus_encode_with_size<S: io::Write>(data: &[u8], mut s: S) ->
597
644
}
598
645
599
646
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
+
600
674
impl Encodable for Vec < u8 > {
601
675
#[ inline]
602
676
fn consensus_encode < S : io:: Write > ( & self , s : S ) -> Result < usize , io:: Error > {
@@ -606,14 +680,15 @@ impl Encodable for Vec<u8> {
606
680
607
681
impl Decodable for Vec < u8 > {
608
682
#[ 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 > {
610
684
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 ) )
617
692
}
618
693
}
619
694
@@ -625,9 +700,14 @@ impl Encodable for Box<[u8]> {
625
700
}
626
701
627
702
impl 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
+
628
708
#[ inline]
629
709
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 ) )
631
711
}
632
712
}
633
713
@@ -651,17 +731,11 @@ impl Encodable for CheckedData {
651
731
652
732
impl Decodable for CheckedData {
653
733
#[ 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 } ) ?;
665
739
let expected_checksum = sha2_checksum ( & ret) ;
666
740
if expected_checksum != checksum {
667
741
Err ( self :: Error :: InvalidChecksum {
@@ -672,6 +746,10 @@ impl Decodable for CheckedData {
672
746
Ok ( CheckedData ( ret) )
673
747
}
674
748
}
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
+ }
675
753
}
676
754
677
755
// References
@@ -1064,5 +1142,21 @@ mod tests {
1064
1142
1065
1143
}
1066
1144
}
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
+
1067
1161
}
1068
1162
0 commit comments