@@ -568,13 +568,16 @@ impl<'s> Parser<'s> {
568568
569569 if self . eat ( b'B' ) {
570570 self . backref ( ) ?;
571+
572+ self . pop_depth ( ) ;
571573 return Ok ( ( ) ) ;
572574 }
573575
574576 let ty_tag = self . next ( ) ?;
575577
576578 if ty_tag == b'p' {
577579 // We don't encode the type if the value is a placeholder.
580+ self . pop_depth ( ) ;
578581 return Ok ( ( ) ) ;
579582 }
580583
@@ -653,16 +656,6 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
653656 }
654657 }
655658
656- fn push_depth ( & mut self ) -> bool {
657- match self . parser {
658- Err ( _) => false ,
659- Ok ( ref mut parser) => {
660- let _ = parser. push_depth ( ) ;
661- true
662- }
663- }
664- }
665-
666659 fn pop_depth ( & mut self ) {
667660 if let Ok ( ref mut parser) = self . parser {
668661 parser. pop_depth ( ) ;
@@ -740,6 +733,8 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
740733 }
741734
742735 fn print_path ( & mut self , in_value : bool ) -> fmt:: Result {
736+ parse ! ( self , push_depth) ;
737+
743738 let tag = parse ! ( self , next) ;
744739 match tag {
745740 b'C' => {
@@ -813,14 +808,12 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
813808 self . out . write_str ( ">" ) ?;
814809 }
815810 b'B' => {
816- let mut backref_printer = self . backref_printer ( ) ;
817- backref_printer. print_path ( in_value) ?;
818- if backref_printer. parser . is_err ( ) {
819- return Err ( fmt:: Error ) ;
820- }
811+ self . backref_printer ( ) . print_path ( in_value) ?;
821812 }
822813 _ => invalid ! ( self ) ,
823814 }
815+
816+ self . pop_depth ( ) ;
824817 Ok ( ( ) )
825818 }
826819
@@ -842,7 +835,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
842835 return self . out . write_str ( ty) ;
843836 }
844837
845- self . push_depth ( ) ;
838+ parse ! ( self , push_depth) ;
846839
847840 match tag {
848841 b'R' | b'Q' => {
@@ -1009,15 +1002,22 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
10091002 }
10101003
10111004 fn print_const ( & mut self ) -> fmt:: Result {
1005+ parse ! ( self , push_depth) ;
1006+
10121007 if self . eat ( b'B' ) {
1013- return self . backref_printer ( ) . print_const ( ) ;
1008+ self . backref_printer ( ) . print_const ( ) ?;
1009+
1010+ self . pop_depth ( ) ;
1011+ return Ok ( ( ) ) ;
10141012 }
10151013
10161014 let ty_tag = parse ! ( self , next) ;
10171015
10181016 if ty_tag == b'p' {
10191017 // We don't encode the type if the value is a placeholder.
10201018 self . out . write_str ( "_" ) ?;
1019+
1020+ self . pop_depth ( ) ;
10211021 return Ok ( ( ) ) ;
10221022 }
10231023
@@ -1041,6 +1041,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
10411041 self . out . write_str ( ty) ?;
10421042 }
10431043
1044+ self . pop_depth ( ) ;
10441045 Ok ( ( ) )
10451046 }
10461047
@@ -1810,4 +1811,37 @@ RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRB_E"
18101811 t_nohash ! ( & sym, expected) ;
18111812 }
18121813 }
1814+
1815+ #[ test]
1816+ fn recursion_limit_backref_free_bypass ( ) {
1817+ // NOTE(eddyb) this test checks that long symbols cannot bypass the
1818+ // recursion limit by not using backrefs, and cause a stack overflow.
1819+
1820+ // This value was chosen to be high enough that stack overflows were
1821+ // observed even with `cargo test --release`.
1822+ let depth = 100_000 ;
1823+
1824+ // In order to hide the long mangling from the initial "shallow" parse,
1825+ // it's nested in an identifier (crate name), preceding its use.
1826+ let mut sym = format ! ( "_RIC{}" , depth) ;
1827+ let backref_start = sym. len ( ) - 2 ;
1828+ for _ in 0 ..depth {
1829+ sym. push ( 'R' ) ;
1830+ }
1831+
1832+ // Write a backref to just after the length of the identifier.
1833+ sym. push ( 'B' ) ;
1834+ sym. push ( char:: from_digit ( ( backref_start - 1 ) as u32 , 36 ) . unwrap ( ) ) ;
1835+ sym. push ( '_' ) ;
1836+
1837+ // Close the `I` at the start.
1838+ sym. push ( 'E' ) ;
1839+
1840+ let demangled = format ! ( "{:#}" , :: demangle( & sym) ) ;
1841+
1842+ // NOTE(eddyb) the `?` indicates that a parse error was encountered.
1843+ // FIXME(eddyb) replace `v0::Invalid` with a proper `v0::ParseError`,
1844+ // that could show e.g. `<recursion limit reached>` instead of `?`.
1845+ assert_eq ! ( demangled. replace( & [ 'R' , '&' ] [ ..] , "" ) , "::<?>" ) ;
1846+ }
18131847}
0 commit comments