@@ -12,6 +12,11 @@ use super::DecodedLength;
1212
1313use self :: Kind :: { Chunked , Eof , Length } ;
1414
15+ /// Maximum amount of bytes allowed in chunked extensions.
16+ ///
17+ /// This limit is currentlty applied for the entire body, not per chunk.
18+ const CHUNKED_EXTENSIONS_LIMIT : u64 = 1024 * 16 ;
19+
1520/// Decoders to handle different Transfer-Encodings.
1621///
1722/// If a message body does not include a Transfer-Encoding, it *should*
@@ -26,7 +31,11 @@ enum Kind {
2631 /// A Reader used when a Content-Length header is passed with a positive integer.
2732 Length ( u64 ) ,
2833 /// A Reader used when Transfer-Encoding is `chunked`.
29- Chunked ( ChunkedState , u64 ) ,
34+ Chunked {
35+ state : ChunkedState ,
36+ chunk_len : u64 ,
37+ extensions_cnt : u64 ,
38+ } ,
3039 /// A Reader used for responses that don't indicate a length or chunked.
3140 ///
3241 /// The bool tracks when EOF is seen on the transport.
@@ -74,7 +83,11 @@ impl Decoder {
7483
7584 pub ( crate ) fn chunked ( ) -> Decoder {
7685 Decoder {
77- kind : Kind :: Chunked ( ChunkedState :: new ( ) , 0 ) ,
86+ kind : Kind :: Chunked {
87+ state : ChunkedState :: new ( ) ,
88+ chunk_len : 0 ,
89+ extensions_cnt : 0 ,
90+ } ,
7891 }
7992 }
8093
@@ -97,7 +110,12 @@ impl Decoder {
97110 pub ( crate ) fn is_eof ( & self ) -> bool {
98111 matches ! (
99112 self . kind,
100- Length ( 0 ) | Chunked ( ChunkedState :: End , _) | Eof ( true )
113+ Length ( 0 )
114+ | Chunked {
115+ state: ChunkedState :: End ,
116+ ..
117+ }
118+ | Eof ( true )
101119 )
102120 }
103121
@@ -128,11 +146,15 @@ impl Decoder {
128146 Poll :: Ready ( Ok ( buf) )
129147 }
130148 }
131- Chunked ( ref mut state, ref mut size) => {
149+ Chunked {
150+ ref mut state,
151+ ref mut chunk_len,
152+ ref mut extensions_cnt,
153+ } => {
132154 loop {
133155 let mut buf = None ;
134156 // advances the chunked state
135- * state = ready ! ( state. step( cx, body, size , & mut buf) ) ?;
157+ * state = ready ! ( state. step( cx, body, chunk_len , extensions_cnt , & mut buf) ) ?;
136158 if * state == ChunkedState :: End {
137159 trace ! ( "end of chunked" ) ;
138160 return Poll :: Ready ( Ok ( Bytes :: new ( ) ) ) ;
@@ -203,14 +225,15 @@ impl ChunkedState {
203225 cx : & mut Context < ' _ > ,
204226 body : & mut R ,
205227 size : & mut u64 ,
228+ extensions_cnt : & mut u64 ,
206229 buf : & mut Option < Bytes > ,
207230 ) -> Poll < Result < ChunkedState , io:: Error > > {
208231 use self :: ChunkedState :: * ;
209232 match * self {
210233 Start => ChunkedState :: read_start ( cx, body, size) ,
211234 Size => ChunkedState :: read_size ( cx, body, size) ,
212235 SizeLws => ChunkedState :: read_size_lws ( cx, body) ,
213- Extension => ChunkedState :: read_extension ( cx, body) ,
236+ Extension => ChunkedState :: read_extension ( cx, body, extensions_cnt ) ,
214237 SizeLf => ChunkedState :: read_size_lf ( cx, body, * size) ,
215238 Body => ChunkedState :: read_body ( cx, body, size, buf) ,
216239 BodyCr => ChunkedState :: read_body_cr ( cx, body) ,
@@ -307,6 +330,7 @@ impl ChunkedState {
307330 fn read_extension < R : MemRead > (
308331 cx : & mut Context < ' _ > ,
309332 rdr : & mut R ,
333+ extensions_cnt : & mut u64 ,
310334 ) -> Poll < Result < ChunkedState , io:: Error > > {
311335 trace ! ( "read_extension" ) ;
312336 // We don't care about extensions really at all. Just ignore them.
@@ -321,7 +345,17 @@ impl ChunkedState {
321345 io:: ErrorKind :: InvalidData ,
322346 "invalid chunk extension contains newline" ,
323347 ) ) ) ,
324- _ => Poll :: Ready ( Ok ( ChunkedState :: Extension ) ) , // no supported extensions
348+ _ => {
349+ * extensions_cnt += 1 ;
350+ if * extensions_cnt >= CHUNKED_EXTENSIONS_LIMIT {
351+ Poll :: Ready ( Err ( io:: Error :: new (
352+ io:: ErrorKind :: InvalidData ,
353+ "chunk extensions over limit" ,
354+ ) ) )
355+ } else {
356+ Poll :: Ready ( Ok ( ChunkedState :: Extension ) )
357+ }
358+ } // no supported extensions
325359 }
326360 }
327361 fn read_size_lf < R : MemRead > (
@@ -492,7 +526,6 @@ mod tests {
492526 }
493527 }
494528
495- #[ cfg( feature = "nightly" ) ]
496529 impl MemRead for Bytes {
497530 fn read_mem ( & mut self , _: & mut Context < ' _ > , len : usize ) -> Poll < io:: Result < Bytes > > {
498531 let n = std:: cmp:: min ( len, self . len ( ) ) ;
@@ -519,10 +552,12 @@ mod tests {
519552 let mut state = ChunkedState :: new ( ) ;
520553 let rdr = & mut s. as_bytes ( ) ;
521554 let mut size = 0 ;
555+ let mut ext_cnt = 0 ;
522556 loop {
523- let result =
524- futures_util:: future:: poll_fn ( |cx| state. step ( cx, rdr, & mut size, & mut None ) )
525- . await ;
557+ let result = futures_util:: future:: poll_fn ( |cx| {
558+ state. step ( cx, rdr, & mut size, & mut ext_cnt, & mut None )
559+ } )
560+ . await ;
526561 let desc = format ! ( "read_size failed for {:?}" , s) ;
527562 state = result. expect ( desc. as_str ( ) ) ;
528563 if state == ChunkedState :: Body || state == ChunkedState :: EndCr {
@@ -536,10 +571,12 @@ mod tests {
536571 let mut state = ChunkedState :: new ( ) ;
537572 let rdr = & mut s. as_bytes ( ) ;
538573 let mut size = 0 ;
574+ let mut ext_cnt = 0 ;
539575 loop {
540- let result =
541- futures_util:: future:: poll_fn ( |cx| state. step ( cx, rdr, & mut size, & mut None ) )
542- . await ;
576+ let result = futures_util:: future:: poll_fn ( |cx| {
577+ state. step ( cx, rdr, & mut size, & mut ext_cnt, & mut None )
578+ } )
579+ . await ;
543580 state = match result {
544581 Ok ( s) => s,
545582 Err ( e) => {
@@ -629,6 +666,33 @@ mod tests {
629666 assert_eq ! ( "1234567890abcdef" , & result) ;
630667 }
631668
669+ #[ tokio:: test]
670+ async fn test_read_chunked_extensions_over_limit ( ) {
671+ // construct a chunked body where each individual chunked extension
672+ // is totally fine, but combined is over the limit.
673+ let per_chunk = super :: CHUNKED_EXTENSIONS_LIMIT * 2 / 3 ;
674+ let mut scratch = vec ! [ ] ;
675+ for _ in 0 ..2 {
676+ scratch. extend ( b"1;" ) ;
677+ scratch. extend ( b"x" . repeat ( per_chunk as usize ) ) ;
678+ scratch. extend ( b"\r \n A\r \n " ) ;
679+ }
680+ scratch. extend ( b"0\r \n \r \n " ) ;
681+ let mut mock_buf = Bytes :: from ( scratch) ;
682+
683+ let mut decoder = Decoder :: chunked ( ) ;
684+ let buf1 = decoder. decode_fut ( & mut mock_buf) . await . expect ( "decode1" ) ;
685+ assert_eq ! ( & buf1[ ..] , b"A" ) ;
686+
687+ let err = decoder
688+ . decode_fut ( & mut mock_buf)
689+ . await
690+ . expect_err ( "decode2" ) ;
691+ assert_eq ! ( err. kind( ) , io:: ErrorKind :: InvalidData ) ;
692+ assert_eq ! ( err. to_string( ) , "chunk extensions over limit" ) ;
693+ }
694+
695+ #[ cfg( not( miri) ) ]
632696 #[ tokio:: test]
633697 async fn test_read_chunked_trailer_with_missing_lf ( ) {
634698 let mut mock_buf = & b"10\r \n 1234567890abcdef\r \n 0\r \n bad\r \r \n " [ ..] ;
0 commit comments