@@ -22,8 +22,8 @@ use crate::{
2222 NO_PREFIX ,
2323 } ,
2424 qpack_send_buf:: Encoder as _,
25- reader:: { ReceiverBufferWrapper , parse_utf8} ,
26- table:: HeaderTable ,
25+ reader:: { LiteralReader , ReceiverBufferWrapper , parse_utf8} ,
26+ table:: { ADDITIONAL_TABLE_ENTRY_SIZE , HeaderTable } ,
2727} ;
2828
2929#[ derive( Default , Debug , PartialEq , Eq ) ]
@@ -210,47 +210,42 @@ impl<'a> HeaderDecoder<'a> {
210210 return Ok ( HeaderDecoderResult :: Blocked ( self . req_insert_cnt ) ) ;
211211 }
212212 let mut h: Vec < Header > = Vec :: new ( ) ;
213+ let mut remaining = LiteralReader :: MAX_LEN ;
213214
214215 while !self . buf . done ( ) {
215216 let b = Error :: map_error ( self . buf . peek ( ) , Error :: Decompression ) ?;
216- if HEADER_FIELD_INDEX_STATIC . cmp_prefix ( b) {
217- h. push ( Error :: map_error (
218- self . read_indexed_static ( ) ,
219- Error :: Decompression ,
220- ) ?) ;
217+ let header = if HEADER_FIELD_INDEX_STATIC . cmp_prefix ( b) {
218+ Error :: map_error ( self . read_indexed_static ( ) , Error :: Decompression ) ?
221219 } else if HEADER_FIELD_INDEX_DYNAMIC . cmp_prefix ( b) {
222- h. push ( Error :: map_error (
223- self . read_indexed_dynamic ( table) ,
224- Error :: Decompression ,
225- ) ?) ;
220+ Error :: map_error ( self . read_indexed_dynamic ( table) , Error :: Decompression ) ?
226221 } else if HEADER_FIELD_INDEX_DYNAMIC_POST . cmp_prefix ( b) {
227- h. push ( Error :: map_error (
228- self . read_indexed_dynamic_post ( table) ,
229- Error :: Decompression ,
230- ) ?) ;
222+ Error :: map_error ( self . read_indexed_dynamic_post ( table) , Error :: Decompression ) ?
231223 } else if HEADER_FIELD_LITERAL_NAME_REF_STATIC . cmp_prefix ( b) {
232- h . push ( Error :: map_error (
224+ Error :: map_error (
233225 self . read_literal_with_name_ref_static ( ) ,
234226 Error :: Decompression ,
235- ) ?) ;
227+ ) ?
236228 } else if HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC . cmp_prefix ( b) {
237- h . push ( Error :: map_error (
229+ Error :: map_error (
238230 self . read_literal_with_name_ref_dynamic ( table) ,
239231 Error :: Decompression ,
240- ) ?) ;
232+ ) ?
241233 } else if HEADER_FIELD_LITERAL_NAME_LITERAL . cmp_prefix ( b) {
242- h. push ( Error :: map_error (
243- self . read_literal_with_name_literal ( ) ,
244- Error :: Decompression ,
245- ) ?) ;
234+ Error :: map_error ( self . read_literal_with_name_literal ( ) , Error :: Decompression ) ?
246235 } else if HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC_POST . cmp_prefix ( b) {
247- h . push ( Error :: map_error (
236+ Error :: map_error (
248237 self . read_literal_with_name_ref_dynamic_post ( table) ,
249238 Error :: Decompression ,
250- ) ?) ;
239+ ) ?
251240 } else {
252241 unreachable ! ( "All prefixes are covered" ) ;
253- }
242+ } ;
243+ remaining = remaining
244+ . checked_sub (
245+ header. name ( ) . len ( ) + header. value ( ) . len ( ) + ADDITIONAL_TABLE_ENTRY_SIZE ,
246+ )
247+ . ok_or ( Error :: Decompression ) ?;
248+ h. push ( header) ;
254249 }
255250
256251 qtrace ! ( "[{self}] done decoding header block" ) ;
@@ -393,7 +388,10 @@ impl<'a> HeaderDecoder<'a> {
393388#[ cfg_attr( coverage_nightly, coverage( off) ) ]
394389mod tests {
395390
396- use super :: { HeaderDecoder , HeaderDecoderResult , HeaderEncoder , HeaderTable } ;
391+ use super :: {
392+ ADDITIONAL_TABLE_ENTRY_SIZE , HeaderDecoder , HeaderDecoderResult , HeaderEncoder ,
393+ HeaderTable , LiteralReader ,
394+ } ;
397395 use crate :: Error ;
398396
399397 const INDEX_STATIC_TEST : & [ ( u64 , & [ u8 ] , & str , & str ) ] = & [
@@ -927,4 +925,49 @@ mod tests {
927925 decoder_h. decode_header_block( & table, 1000 , 0 ) . unwrap_err( )
928926 ) ;
929927 }
928+
929+ /// A header block that references the same static table entry many times must be
930+ /// rejected before exhausting memory.
931+ #[ test]
932+ fn header_list_size_limit ( ) {
933+ let table = HeaderTable :: new ( false ) ;
934+ // 0xC0 = HEADER_FIELD_INDEX_STATIC with index 0 (:authority, "").
935+ let entry_size = ":authority" . len ( ) + ADDITIONAL_TABLE_ENTRY_SIZE ;
936+ let reps = LiteralReader :: MAX_LEN / entry_size + 1 ;
937+ // Prefix: required_insert_cnt=0, base_delta=0 (2 bytes: 0x00, 0x00)
938+ let mut buf = vec ! [ 0x00u8 , 0x00 ] ;
939+ buf. resize ( buf. len ( ) + reps, 0xC0 ) ;
940+ let mut decoder_h = HeaderDecoder :: new ( & buf) ;
941+ assert_eq ! (
942+ Error :: Decompression ,
943+ decoder_h. decode_header_block( & table, 1000 , 0 ) . unwrap_err( )
944+ ) ;
945+ }
946+
947+ /// Truncated input for each header-block prefix type must propagate as
948+ /// `Error::Decompression`. Each byte sets the maximum value for its prefix's
949+ /// integer field, forcing a continuation byte that is not present.
950+ #[ test]
951+ fn decode_truncated_for_each_prefix ( ) {
952+ const TRUNCATED_PREFIXES : & [ u8 ] = & [
953+ 0xFF , // HEADER_FIELD_INDEX_STATIC
954+ 0xBF , // HEADER_FIELD_INDEX_DYNAMIC
955+ 0x1F , // HEADER_FIELD_INDEX_DYNAMIC_POST
956+ 0x5F , // HEADER_FIELD_LITERAL_NAME_REF_STATIC
957+ 0x4F , // HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC
958+ 0x3F , // HEADER_FIELD_LITERAL_NAME_LITERAL
959+ 0x07 , // HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC_POST
960+ ] ;
961+ let mut table = HeaderTable :: new ( false ) ;
962+ fill_table ( & mut table) ;
963+ for & prefix in TRUNCATED_PREFIXES {
964+ let buf = [ 0x00 , 0x00 , prefix] ;
965+ let mut decoder_h = HeaderDecoder :: new ( & buf) ;
966+ assert_eq ! (
967+ Error :: Decompression ,
968+ decoder_h. decode_header_block( & table, 1000 , 0 ) . unwrap_err( ) ,
969+ "prefix {prefix:#04x}"
970+ ) ;
971+ }
972+ }
930973}
0 commit comments