Skip to content

Commit d7ea01a

Browse files
authored
v0: don't ignore recursion limit failures from any push_depth calls. (#52)
1 parent cce8a07 commit d7ea01a

File tree

2 files changed

+62
-20
lines changed

2 files changed

+62
-20
lines changed

src/lib.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -418,9 +418,17 @@ mod tests {
418418

419419
#[test]
420420
fn limit_recursion() {
421-
use std::fmt::Write;
422-
let mut s = String::new();
423-
assert!(write!(s, "{}", super::demangle("_RNvB_1a")).is_err());
421+
// NOTE(eddyb) the `?` indicate that a parse error was encountered.
422+
// FIXME(eddyb) replace `v0::Invalid` with a proper `v0::ParseError`,
423+
// that could show e.g. `<recursion limit reached>` instead of `?`.
424+
assert_eq!(
425+
super::demangle("_RNvB_1a").to_string().replace("::a", ""),
426+
"?"
427+
);
428+
assert_eq!(
429+
super::demangle("_RMC0RB2_").to_string().replace("&", ""),
430+
"<?>"
431+
);
424432
}
425433

426434
#[test]

src/v0.rs

+51-17
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)