diff --git a/src/lib.rs b/src/lib.rs index 775ad12..64c30ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -418,9 +418,17 @@ mod tests { #[test] fn limit_recursion() { - use std::fmt::Write; - let mut s = String::new(); - assert!(write!(s, "{}", super::demangle("_RNvB_1a")).is_err()); + // NOTE(eddyb) the `?` indicate that a parse error was encountered. + // FIXME(eddyb) replace `v0::Invalid` with a proper `v0::ParseError`, + // that could show e.g. `` instead of `?`. + assert_eq!( + super::demangle("_RNvB_1a").to_string().replace("::a", ""), + "?" + ); + assert_eq!( + super::demangle("_RMC0RB2_").to_string().replace("&", ""), + "" + ); } #[test] diff --git a/src/v0.rs b/src/v0.rs index bc2f9b6..b07f182 100644 --- a/src/v0.rs +++ b/src/v0.rs @@ -568,6 +568,8 @@ impl<'s> Parser<'s> { if self.eat(b'B') { self.backref()?; + + self.pop_depth(); return Ok(()); } @@ -575,6 +577,7 @@ impl<'s> Parser<'s> { if ty_tag == b'p' { // We don't encode the type if the value is a placeholder. + self.pop_depth(); return Ok(()); } @@ -653,16 +656,6 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { } } - fn push_depth(&mut self) -> bool { - match self.parser { - Err(_) => false, - Ok(ref mut parser) => { - let _ = parser.push_depth(); - true - } - } - } - fn pop_depth(&mut self) { if let Ok(ref mut parser) = self.parser { parser.pop_depth(); @@ -740,6 +733,8 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { } fn print_path(&mut self, in_value: bool) -> fmt::Result { + parse!(self, push_depth); + let tag = parse!(self, next); match tag { b'C' => { @@ -813,14 +808,12 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { self.out.write_str(">")?; } b'B' => { - let mut backref_printer = self.backref_printer(); - backref_printer.print_path(in_value)?; - if backref_printer.parser.is_err() { - return Err(fmt::Error); - } + self.backref_printer().print_path(in_value)?; } _ => invalid!(self), } + + self.pop_depth(); Ok(()) } @@ -842,7 +835,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { return self.out.write_str(ty); } - self.push_depth(); + parse!(self, push_depth); match tag { b'R' | b'Q' => { @@ -1009,8 +1002,13 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { } fn print_const(&mut self) -> fmt::Result { + parse!(self, push_depth); + if self.eat(b'B') { - return self.backref_printer().print_const(); + self.backref_printer().print_const()?; + + self.pop_depth(); + return Ok(()); } let ty_tag = parse!(self, next); @@ -1018,6 +1016,8 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { if ty_tag == b'p' { // We don't encode the type if the value is a placeholder. self.out.write_str("_")?; + + self.pop_depth(); return Ok(()); } @@ -1041,6 +1041,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { self.out.write_str(ty)?; } + self.pop_depth(); Ok(()) } @@ -1810,4 +1811,37 @@ RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRB_E" t_nohash!(&sym, expected); } } + + #[test] + fn recursion_limit_backref_free_bypass() { + // NOTE(eddyb) this test checks that long symbols cannot bypass the + // recursion limit by not using backrefs, and cause a stack overflow. + + // This value was chosen to be high enough that stack overflows were + // observed even with `cargo test --release`. + let depth = 100_000; + + // In order to hide the long mangling from the initial "shallow" parse, + // it's nested in an identifier (crate name), preceding its use. + let mut sym = format!("_RIC{}", depth); + let backref_start = sym.len() - 2; + for _ in 0..depth { + sym.push('R'); + } + + // Write a backref to just after the length of the identifier. + sym.push('B'); + sym.push(char::from_digit((backref_start - 1) as u32, 36).unwrap()); + sym.push('_'); + + // Close the `I` at the start. + sym.push('E'); + + let demangled = format!("{:#}", ::demangle(&sym)); + + // NOTE(eddyb) the `?` indicates that a parse error was encountered. + // FIXME(eddyb) replace `v0::Invalid` with a proper `v0::ParseError`, + // that could show e.g. `` instead of `?`. + assert_eq!(demangled.replace(&['R', '&'][..], ""), "::"); + } }