@@ -11,6 +11,11 @@ use super::DecodedLength;
1111
1212use self :: Kind :: { Chunked , Eof , Length } ;
1313
14+ /// Maximum amount of bytes allowed in chunked extensions.
15+ ///
16+ /// This limit is currentlty applied for the entire body, not per chunk.
17+ const CHUNKED_EXTENSIONS_LIMIT : u64 = 1024 * 16 ;
18+
1419/// Decoders to handle different Transfer-Encodings.
1520///
1621/// If a message body does not include a Transfer-Encoding, it *should*
@@ -25,7 +30,11 @@ enum Kind {
2530 /// A Reader used when a Content-Length header is passed with a positive integer.
2631 Length ( u64 ) ,
2732 /// A Reader used when Transfer-Encoding is `chunked`.
28- Chunked ( ChunkedState , u64 ) ,
33+ Chunked {
34+ state : ChunkedState ,
35+ chunk_len : u64 ,
36+ extensions_cnt : u64 ,
37+ } ,
2938 /// A Reader used for responses that don't indicate a length or chunked.
3039 ///
3140 /// The bool tracks when EOF is seen on the transport.
@@ -73,7 +82,11 @@ impl Decoder {
7382
7483 pub ( crate ) fn chunked ( ) -> Decoder {
7584 Decoder {
76- kind : Kind :: Chunked ( ChunkedState :: new ( ) , 0 ) ,
85+ kind : Kind :: Chunked {
86+ state : ChunkedState :: new ( ) ,
87+ chunk_len : 0 ,
88+ extensions_cnt : 0 ,
89+ } ,
7790 }
7891 }
7992
@@ -96,7 +109,12 @@ impl Decoder {
96109 pub ( crate ) fn is_eof ( & self ) -> bool {
97110 matches ! (
98111 self . kind,
99- Length ( 0 ) | Chunked ( ChunkedState :: End , _) | Eof ( true )
112+ Length ( 0 )
113+ | Chunked {
114+ state: ChunkedState :: End ,
115+ ..
116+ }
117+ | Eof ( true )
100118 )
101119 }
102120
@@ -127,11 +145,15 @@ impl Decoder {
127145 Poll :: Ready ( Ok ( buf) )
128146 }
129147 }
130- Chunked ( ref mut state, ref mut size) => {
148+ Chunked {
149+ ref mut state,
150+ ref mut chunk_len,
151+ ref mut extensions_cnt,
152+ } => {
131153 loop {
132154 let mut buf = None ;
133155 // advances the chunked state
134- * state = ready ! ( state. step( cx, body, size , & mut buf) ) ?;
156+ * state = ready ! ( state. step( cx, body, chunk_len , extensions_cnt , & mut buf) ) ?;
135157 if * state == ChunkedState :: End {
136158 trace ! ( "end of chunked" ) ;
137159 return Poll :: Ready ( Ok ( Bytes :: new ( ) ) ) ;
@@ -202,14 +224,15 @@ impl ChunkedState {
202224 cx : & mut Context < ' _ > ,
203225 body : & mut R ,
204226 size : & mut u64 ,
227+ extensions_cnt : & mut u64 ,
205228 buf : & mut Option < Bytes > ,
206229 ) -> Poll < Result < ChunkedState , io:: Error > > {
207230 use self :: ChunkedState :: * ;
208231 match * self {
209232 Start => ChunkedState :: read_start ( cx, body, size) ,
210233 Size => ChunkedState :: read_size ( cx, body, size) ,
211234 SizeLws => ChunkedState :: read_size_lws ( cx, body) ,
212- Extension => ChunkedState :: read_extension ( cx, body) ,
235+ Extension => ChunkedState :: read_extension ( cx, body, extensions_cnt ) ,
213236 SizeLf => ChunkedState :: read_size_lf ( cx, body, * size) ,
214237 Body => ChunkedState :: read_body ( cx, body, size, buf) ,
215238 BodyCr => ChunkedState :: read_body_cr ( cx, body) ,
@@ -306,6 +329,7 @@ impl ChunkedState {
306329 fn read_extension < R : MemRead > (
307330 cx : & mut Context < ' _ > ,
308331 rdr : & mut R ,
332+ extensions_cnt : & mut u64 ,
309333 ) -> Poll < Result < ChunkedState , io:: Error > > {
310334 trace ! ( "read_extension" ) ;
311335 // We don't care about extensions really at all. Just ignore them.
@@ -320,7 +344,17 @@ impl ChunkedState {
320344 io:: ErrorKind :: InvalidData ,
321345 "invalid chunk extension contains newline" ,
322346 ) ) ) ,
323- _ => Poll :: Ready ( Ok ( ChunkedState :: Extension ) ) , // no supported extensions
347+ _ => {
348+ * extensions_cnt += 1 ;
349+ if * extensions_cnt >= CHUNKED_EXTENSIONS_LIMIT {
350+ Poll :: Ready ( Err ( io:: Error :: new (
351+ io:: ErrorKind :: InvalidData ,
352+ "chunk extensions over limit" ,
353+ ) ) )
354+ } else {
355+ Poll :: Ready ( Ok ( ChunkedState :: Extension ) )
356+ }
357+ } // no supported extensions
324358 }
325359 }
326360 fn read_size_lf < R : MemRead > (
@@ -491,7 +525,6 @@ mod tests {
491525 }
492526 }
493527
494- #[ cfg( feature = "nightly" ) ]
495528 impl MemRead for Bytes {
496529 fn read_mem ( & mut self , _: & mut Context < ' _ > , len : usize ) -> Poll < io:: Result < Bytes > > {
497530 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) => {
@@ -632,6 +669,32 @@ mod tests {
632669 assert_eq ! ( "1234567890abcdef" , & result) ;
633670 }
634671
672+ #[ tokio:: test]
673+ async fn test_read_chunked_extensions_over_limit ( ) {
674+ // construct a chunked body where each individual chunked extension
675+ // is totally fine, but combined is over the limit.
676+ let per_chunk = super :: CHUNKED_EXTENSIONS_LIMIT * 2 / 3 ;
677+ let mut scratch = vec ! [ ] ;
678+ for _ in 0 ..2 {
679+ scratch. extend ( b"1;" ) ;
680+ scratch. extend ( b"x" . repeat ( per_chunk as usize ) ) ;
681+ scratch. extend ( b"\r \n A\r \n " ) ;
682+ }
683+ scratch. extend ( b"0\r \n \r \n " ) ;
684+ let mut mock_buf = Bytes :: from ( scratch) ;
685+
686+ let mut decoder = Decoder :: chunked ( ) ;
687+ let buf1 = decoder. decode_fut ( & mut mock_buf) . await . expect ( "decode1" ) ;
688+ assert_eq ! ( & buf1[ ..] , b"A" ) ;
689+
690+ let err = decoder
691+ . decode_fut ( & mut mock_buf)
692+ . await
693+ . expect_err ( "decode2" ) ;
694+ assert_eq ! ( err. kind( ) , io:: ErrorKind :: InvalidData ) ;
695+ assert_eq ! ( err. to_string( ) , "chunk extensions over limit" ) ;
696+ }
697+
635698 #[ cfg( not( miri) ) ]
636699 #[ tokio:: test]
637700 async fn test_read_chunked_trailer_with_missing_lf ( ) {
0 commit comments