@@ -612,30 +612,48 @@ def _parse_subrs(self, tokens, _data):
612612 f"Token following /Subrs must be a number, was { count_token } "
613613 )
614614 count = count_token .value ()
615- array = [None ] * count
616615 next (t for t in tokens if t .is_keyword ('array' ))
617- for _ in range (count ):
618- next (t for t in tokens if t .is_keyword ('dup' ))
619- index_token = next (tokens )
620- if not index_token .is_number ():
621- raise RuntimeError (
622- "Token following dup in Subrs definition must be a "
623- f"number, was { index_token } "
624- )
625- nbytes_token = next (tokens )
626- if not nbytes_token .is_number ():
627- raise RuntimeError (
628- "Second token following dup in Subrs definition must "
629- f"be a number, was { nbytes_token } "
630- )
631- token = next (tokens )
632- if not token .is_keyword (self ._abbr ['RD' ]):
633- raise RuntimeError (
634- f"Token preceding subr must be { self ._abbr ['RD' ]} , "
635- f"was { token } "
636- )
637- binary_token = tokens .send (1 + nbytes_token .value ())
638- array [index_token .value ()] = binary_token .value ()
616+ # Accumulate the parsed subrs into a dict and only allocate the result
617+ # list once the body has been read. Allocating ``[None] * count`` up
618+ # front lets a malformed font declare a huge count in a few bytes and
619+ # force a large allocation before it is rejected.
620+ entries = {}
621+ try :
622+ for _ in range (count ):
623+ next (t for t in tokens if t .is_keyword ('dup' ))
624+ index_token = next (tokens )
625+ if not index_token .is_number ():
626+ raise RuntimeError (
627+ "Token following dup in Subrs definition must be a "
628+ f"number, was { index_token } "
629+ )
630+ nbytes_token = next (tokens )
631+ if not nbytes_token .is_number ():
632+ raise RuntimeError (
633+ "Second token following dup in Subrs definition must "
634+ f"be a number, was { nbytes_token } "
635+ )
636+ token = next (tokens )
637+ if not token .is_keyword (self ._abbr ['RD' ]):
638+ raise RuntimeError (
639+ f"Token preceding subr must be { self ._abbr ['RD' ]} , "
640+ f"was { token } "
641+ )
642+ binary_token = tokens .send (1 + nbytes_token .value ())
643+ entries [index_token .value ()] = binary_token .value ()
644+ except StopIteration :
645+ raise RuntimeError (
646+ "Malformed Type1 font file: Incomplete /Subrs"
647+ ) from None
648+
649+ # The indices must cover 0 to count-1 exactly.
650+ if (len (entries ) != count
651+ or (count and (min (entries ), max (entries )) != (0 , count - 1 ))):
652+ raise RuntimeError (
653+ "Malformed Type1 font file: /Subrs indices do not cover "
654+ f"0 to { count - 1 } "
655+ )
656+ array = [entries [index ] for index in range (count )]
639657
640658 return array , next (tokens ).endpos ()
641659
0 commit comments