Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parser stack overflow with very deeply nested items #32594

Closed
comex opened this issue Mar 29, 2016 · 6 comments · Fixed by #55617
Closed

parser stack overflow with very deeply nested items #32594

comex opened this issue Mar 29, 2016 · 6 comments · Fixed by #55617
Labels
A-parser Area: The parsing of Rust source code to an AST. C-bug Category: This is a bug. I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@comex
Copy link
Contributor

comex commented Mar 29, 2016

Trying to compile this code, which contains 492 nested loop blocks: http://pastie.org/private/blihpoctk64fvv9yvwkp5g

causes rustc to unceremoniously segfault. I feel like this is basic enough (unavoidable other than with diagnostics?) that there should be an existing issue for it, but I can't find one, so here's a new report.

For the record, this is stripped down from real (generated) code. I have been using labeled break as a substitute for goto - in my real code the loop blocks are labeled; after each loop is the code for that case, followed by a return. Inside all the nested loops is a decoder which breaks to one of the labels, consisting of a number of nested match statements, within which a single label can appear several times. (Normally I would use functions instead of labels, like return handle_foo(...), and in fact this might 'just work', but I can't guarantee that each helper function (1) is inlined, but (2) is inlined only once, after the calls are merged into one basic block, rather than separately for each time it's mentioned.)

Partial backtrace:

  * frame #0: 0x0000000101cdd01a libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_item_::h7cfdc3a522ccefe4 + 26
    frame #1: 0x0000000101cdcfd3 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::with_res::hca18e2b367aac45a + 83
    frame #2: 0x0000000101cda76d libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_stmt_::h2ef05e1acf23b3ee + 2749
    frame #3: 0x0000000101cc2685 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_block_tail::h850fd6dee5511129 + 181
    frame #4: 0x0000000101ca33da libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_inner_attrs_and_block::h758cb08b805a3c04 + 1274
    frame #5: 0x0000000101cc1f10 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_loop_expr::he95eef038af4df5c + 48
    frame #6: 0x0000000101cb9304 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_bottom_expr::hdb91d7fed291df76 + 11332
    frame #7: 0x0000000101cc45b6 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_dot_or_call_expr::hd6b49bfb09b97ce0 + 438
    frame #8: 0x0000000101cce364 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_prefix_expr::h696f8c3b5de4ed7a + 3812
    frame #9: 0x0000000101ccfbe8 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_assoc_expr_with::h437ac6f395ba7253 + 232
    frame #10: 0x0000000101ccfaec libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_assoc_expr::h6d60b59a50fe4e9b + 92
    frame #11: 0x0000000101ccf6f6 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_expr_res::he5eaa4701a507b69 + 86
    frame #12: 0x0000000101cdae2a libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_stmt_::h2ef05e1acf23b3ee + 4474
    frame #13: 0x0000000101cc2685 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_block_tail::h850fd6dee5511129 + 181
    frame #14: 0x0000000101ca33da libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_inner_attrs_and_block::h758cb08b805a3c04 + 1274
    frame #15: 0x0000000101cc1f10 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_loop_expr::he95eef038af4df5c + 48
    frame #16: 0x0000000101cb9304 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_bottom_expr::hdb91d7fed291df76 + 11332
    frame #17: 0x0000000101cc45b6 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_dot_or_call_expr::hd6b49bfb09b97ce0 + 438
    frame #18: 0x0000000101cce364 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_prefix_expr::h696f8c3b5de4ed7a + 3812
    frame #19: 0x0000000101ccfbe8 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_assoc_expr_with::h437ac6f395ba7253 + 232
[..]
    frame #2399: 0x0000000101ccfbe8 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_assoc_expr_with::h437ac6f395ba7253 + 232
    frame #2400: 0x0000000101ccfaec libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_assoc_expr::h6d60b59a50fe4e9b + 92
    frame #2401: 0x0000000101ccf6f6 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_expr_res::he5eaa4701a507b69 + 86
    frame #2402: 0x0000000101cdae2a libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_stmt_::h2ef05e1acf23b3ee + 4474
    frame #2403: 0x0000000101cc2685 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_block_tail::h850fd6dee5511129 + 181
    frame #2404: 0x0000000101ca33da libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_inner_attrs_and_block::h758cb08b805a3c04 + 1274
    frame #2405: 0x0000000101cf77fa libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_item_fn::hce3de4303f5d8ba2 + 1674
    frame #2406: 0x0000000101cdf539 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_item_::h7cfdc3a522ccefe4 + 9529
    frame #2407: 0x0000000101c7a1e4 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_item::h436e395dd151cc52 + 484
    frame #2408: 0x0000000101d068ea libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_mod_items::hea97d1b94833eb22 + 186
    frame #2409: 0x0000000101c78112 libsyntax-18402db3.dylib`syntax::parse::parser::Parser::parse_crate_mod::h8c61b50923a69031 + 466
    frame #2410: 0x0000000101c77d9b libsyntax-18402db3.dylib`syntax::parse::parse_crate_from_file::h2bec29ecacefb78e + 171
    frame #2411: 0x0000000100058780 librustc_driver-18402db3.dylib`rustc_driver::driver::phase_1_parse_input::_$u7b$$u7b$closure$u7d$$u7d$::hf7d19c22fe6fc59c + 176
    frame #2412: 0x000000010002cc5a librustc_driver-18402db3.dylib`rustc_driver::driver::phase_1_parse_input::hddc8900dc2500ee4 + 250
    frame #2413: 0x0000000100019500 librustc_driver-18402db3.dylib`rustc_driver::driver::compile_input::hb8958b270f32a505 + 144
    frame #2414: 0x00000001000091d0 librustc_driver-18402db3.dylib`rustc_driver::run_compiler::h146dabfc96bf3a5a + 4000
    frame #2415: 0x0000000100006673 librustc_driver-18402db3.dylib`std::sys_common::unwind::try::try_fn::h847694ac14db1c2b + 467
    frame #2416: 0x000000010487b25c libstd-18402db3.dylib`__rust_try + 12
    frame #2417: 0x000000010487b1e4 libstd-18402db3.dylib`std::sys_common::unwind::inner_try::h790ed072af125c7c + 116
    frame #2418: 0x0000000100006f0a librustc_driver-18402db3.dylib`_$LT$F$u20$as$u20$alloc..boxed..FnBox$LT$A$GT$$GT$::call_box::h64418c3ccdc3072e + 394
    frame #2419: 0x0000000104888ef9 libstd-18402db3.dylib`std::sys::thread::Thread::new::thread_start::hdaf9c8e3f2f1c46e + 57
    frame #2420: 0x00007fff893ee9b1 libsystem_pthread.dylib`_pthread_body + 131
    frame #2421: 0x00007fff893ee92e libsystem_pthread.dylib`_pthread_start + 168
    frame #2422: 0x00007fff893ec385 libsystem_pthread.dylib`thread_start + 13
@durka
Copy link
Contributor

durka commented Mar 30, 2016

May I suggest match as a substitute for goto?

@durka
Copy link
Contributor

durka commented Mar 30, 2016

Also, if you happen to have more RAM, you can paper over this by setting RUST_MIN_STACK to some large number (the units are bytes) -- your example compiles locally with just over 16MB stack space.

@Mark-Simulacrum
Copy link
Member

I cannot get pastie.org to work for me. I'll revisit in a few days and close if I still can't access at that point...

@Mark-Simulacrum
Copy link
Member

Closing since there's no code to allow reproducing this now.

@comex
Copy link
Contributor Author

comex commented May 5, 2017

Here: https://play.rust-lang.org/?gist=dfc63278a57e83fc2b1020ac087b5a43&version=stable&backtrace=0

That's 10,000 nested loops, but I think you can make it happen sooner by using other constructs in between.

@Mark-Simulacrum Mark-Simulacrum added the I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ label May 5, 2017
@Mark-Simulacrum Mark-Simulacrum added I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. A-parser Area: The parsing of Rust source code to an AST. labels Jun 22, 2017
@Mark-Simulacrum Mark-Simulacrum added the C-bug Category: This is a bug. label Jul 22, 2017
@steveklabnik
Copy link
Member

Triage: still overflows.

bors added a commit that referenced this issue Nov 6, 2018
Prevent compiler stack overflow for deeply recursive code

I was unable to write a test that

1. runs in under 1s
2. overflows on my machine without this patch

The following reproduces the issue, but I don't think it's sensible to include a test that takes 30s to compile. We can now easily squash newly appearing overflows by the strategic insertion of calls to `ensure_sufficient_stack`.

```rust
// compile-pass

#![recursion_limit="1000000"]

macro_rules! chain {
    (EE $e:expr) => {$e.sin()};
    (RECURSE $i:ident $e:expr) => {chain!($i chain!($i chain!($i chain!($i $e))))};
    (Z $e:expr) => {chain!(RECURSE EE $e)};
    (Y $e:expr) => {chain!(RECURSE Z $e)};
    (X $e:expr) => {chain!(RECURSE Y $e)};
    (A $e:expr) => {chain!(RECURSE X $e)};
    (B $e:expr) => {chain!(RECURSE A $e)};
    (C $e:expr) => {chain!(RECURSE B $e)};
    // causes overflow on x86_64 linux
    // less than 1 second until overflow on test machine
    // after overflow has been fixed, takes 30s to compile :/
    (D $e:expr) => {chain!(RECURSE C $e)};
    (E $e:expr) => {chain!(RECURSE D $e)};
    (F $e:expr) => {chain!(RECURSE E $e)};
    // more than 10 seconds
    (G $e:expr) => {chain!(RECURSE F $e)};
    (H $e:expr) => {chain!(RECURSE G $e)};
    (I $e:expr) => {chain!(RECURSE H $e)};
    (J $e:expr) => {chain!(RECURSE I $e)};
    (K $e:expr) => {chain!(RECURSE J $e)};
    (L $e:expr) => {chain!(RECURSE L $e)};
}

fn main() {
    let x = chain!(D 42.0_f32);
}
```

fixes #55471
fixes #41884
fixes #40161
fixes #34844
fixes #32594

cc @alexcrichton @rust-lang/compiler

I looked at all code that checks the recursion limit and inserted stack growth calls where appropriate.
bors added a commit that referenced this issue Nov 13, 2018
Prevent compiler stack overflow for deeply recursive code

I was unable to write a test that

1. runs in under 1s
2. overflows on my machine without this patch

The following reproduces the issue, but I don't think it's sensible to include a test that takes 30s to compile. We can now easily squash newly appearing overflows by the strategic insertion of calls to `ensure_sufficient_stack`.

```rust
// compile-pass

#![recursion_limit="1000000"]

macro_rules! chain {
    (EE $e:expr) => {$e.sin()};
    (RECURSE $i:ident $e:expr) => {chain!($i chain!($i chain!($i chain!($i $e))))};
    (Z $e:expr) => {chain!(RECURSE EE $e)};
    (Y $e:expr) => {chain!(RECURSE Z $e)};
    (X $e:expr) => {chain!(RECURSE Y $e)};
    (A $e:expr) => {chain!(RECURSE X $e)};
    (B $e:expr) => {chain!(RECURSE A $e)};
    (C $e:expr) => {chain!(RECURSE B $e)};
    // causes overflow on x86_64 linux
    // less than 1 second until overflow on test machine
    // after overflow has been fixed, takes 30s to compile :/
    (D $e:expr) => {chain!(RECURSE C $e)};
    (E $e:expr) => {chain!(RECURSE D $e)};
    (F $e:expr) => {chain!(RECURSE E $e)};
    // more than 10 seconds
    (G $e:expr) => {chain!(RECURSE F $e)};
    (H $e:expr) => {chain!(RECURSE G $e)};
    (I $e:expr) => {chain!(RECURSE H $e)};
    (J $e:expr) => {chain!(RECURSE I $e)};
    (K $e:expr) => {chain!(RECURSE J $e)};
    (L $e:expr) => {chain!(RECURSE L $e)};
}

fn main() {
    let x = chain!(D 42.0_f32);
}
```

fixes #55471
fixes #41884
fixes #40161
fixes #34844
fixes #32594

cc @alexcrichton @rust-lang/compiler

I looked at all code that checks the recursion limit and inserted stack growth calls where appropriate.
@jonas-schievink jonas-schievink added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Nov 22, 2019
Centril added a commit to Centril/rust that referenced this issue Mar 18, 2020
Prevent compiler stack overflow for deeply recursive code

I was unable to write a test that

1. runs in under 1s
2. overflows on my machine without this patch

The following reproduces the issue, but I don't think it's sensible to include a test that takes 30s to compile. We can now easily squash newly appearing overflows by the strategic insertion of calls to `ensure_sufficient_stack`.

```rust
// compile-pass

#![recursion_limit="1000000"]

macro_rules! chain {
    (EE $e:expr) => {$e.sin()};
    (RECURSE $i:ident $e:expr) => {chain!($i chain!($i chain!($i chain!($i $e))))};
    (Z $e:expr) => {chain!(RECURSE EE $e)};
    (Y $e:expr) => {chain!(RECURSE Z $e)};
    (X $e:expr) => {chain!(RECURSE Y $e)};
    (A $e:expr) => {chain!(RECURSE X $e)};
    (B $e:expr) => {chain!(RECURSE A $e)};
    (C $e:expr) => {chain!(RECURSE B $e)};
    // causes overflow on x86_64 linux
    // less than 1 second until overflow on test machine
    // after overflow has been fixed, takes 30s to compile :/
    (D $e:expr) => {chain!(RECURSE C $e)};
    (E $e:expr) => {chain!(RECURSE D $e)};
    (F $e:expr) => {chain!(RECURSE E $e)};
    // more than 10 seconds
    (G $e:expr) => {chain!(RECURSE F $e)};
    (H $e:expr) => {chain!(RECURSE G $e)};
    (I $e:expr) => {chain!(RECURSE H $e)};
    (J $e:expr) => {chain!(RECURSE I $e)};
    (K $e:expr) => {chain!(RECURSE J $e)};
    (L $e:expr) => {chain!(RECURSE L $e)};
}

fn main() {
    let x = chain!(D 42.0_f32);
}
```

fixes rust-lang#55471
fixes rust-lang#41884
fixes rust-lang#40161
fixes rust-lang#34844
fixes rust-lang#32594

cc @alexcrichton @rust-lang/compiler

I looked at all code that checks the recursion limit and inserted stack growth calls where appropriate.
@bors bors closed this as completed in 97f3eee May 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-parser Area: The parsing of Rust source code to an AST. C-bug Category: This is a bug. I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants