@@ -568,13 +568,16 @@ impl<'s> Parser<'s> {
568
568
569
569
if self . eat ( b'B' ) {
570
570
self . backref ( ) ?;
571
+
572
+ self . pop_depth ( ) ;
571
573
return Ok ( ( ) ) ;
572
574
}
573
575
574
576
let ty_tag = self . next ( ) ?;
575
577
576
578
if ty_tag == b'p' {
577
579
// We don't encode the type if the value is a placeholder.
580
+ self . pop_depth ( ) ;
578
581
return Ok ( ( ) ) ;
579
582
}
580
583
@@ -653,16 +656,6 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
653
656
}
654
657
}
655
658
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
-
666
659
fn pop_depth ( & mut self ) {
667
660
if let Ok ( ref mut parser) = self . parser {
668
661
parser. pop_depth ( ) ;
@@ -740,6 +733,8 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
740
733
}
741
734
742
735
fn print_path ( & mut self , in_value : bool ) -> fmt:: Result {
736
+ parse ! ( self , push_depth) ;
737
+
743
738
let tag = parse ! ( self , next) ;
744
739
match tag {
745
740
b'C' => {
@@ -813,14 +808,12 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
813
808
self . out . write_str ( ">" ) ?;
814
809
}
815
810
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) ?;
821
812
}
822
813
_ => invalid ! ( self ) ,
823
814
}
815
+
816
+ self . pop_depth ( ) ;
824
817
Ok ( ( ) )
825
818
}
826
819
@@ -842,7 +835,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
842
835
return self . out . write_str ( ty) ;
843
836
}
844
837
845
- self . push_depth ( ) ;
838
+ parse ! ( self , push_depth) ;
846
839
847
840
match tag {
848
841
b'R' | b'Q' => {
@@ -1009,15 +1002,22 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
1009
1002
}
1010
1003
1011
1004
fn print_const ( & mut self ) -> fmt:: Result {
1005
+ parse ! ( self , push_depth) ;
1006
+
1012
1007
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 ( ( ) ) ;
1014
1012
}
1015
1013
1016
1014
let ty_tag = parse ! ( self , next) ;
1017
1015
1018
1016
if ty_tag == b'p' {
1019
1017
// We don't encode the type if the value is a placeholder.
1020
1018
self . out . write_str ( "_" ) ?;
1019
+
1020
+ self . pop_depth ( ) ;
1021
1021
return Ok ( ( ) ) ;
1022
1022
}
1023
1023
@@ -1041,6 +1041,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
1041
1041
self . out . write_str ( ty) ?;
1042
1042
}
1043
1043
1044
+ self . pop_depth ( ) ;
1044
1045
Ok ( ( ) )
1045
1046
}
1046
1047
@@ -1810,4 +1811,37 @@ RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRB_E"
1810
1811
t_nohash ! ( & sym, expected) ;
1811
1812
}
1812
1813
}
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
+ }
1813
1847
}
0 commit comments