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

Overflowing the stack doesn't work, function optimized away #283

Open
Dentosal opened this issue Feb 2, 2017 · 8 comments
Open

Overflowing the stack doesn't work, function optimized away #283

Dentosal opened this issue Feb 2, 2017 · 8 comments
Assignees

Comments

@Dentosal
Copy link
Contributor

@Dentosal Dentosal commented Feb 2, 2017

From Double faults: Kernel Stack Overflow

fn stack_overflow() {
    stack_overflow(); // for each recursion, the return address is pushed
}

// trigger a stack overflow
stack_overflow();

I try to compile this, but xargo build just says:

warning: function cannot return without recurring, #[warn(unconditional_recursion)] on by default
  --> src/rust_os.rs:89:5
   |
89 |     fn stack_overflow() {
   |     ^
   |
note: recursive call site
  --> src/rust_os.rs:90:9
   |
90 |         stack_overflow(); // for each recursion, the return address is pushed
   |         ^^^^^^^^^^^^^^^^
   = help: a `loop` may express intention better if this is on purpose

And then it (or llvm) apparently optimizes the recursion away, and to me it looks like the stack_overflow function wasn't even called, and It did not crash! is printed.

My Rust version is rustc 1.14.0-nightly.

I used this code to work around this:

fn stack_overflow() {
    print!(".");
    stack_overflow();
}
stack_overflow();

Because print!(".") will cause a side-effect, the function must be called.

@phil-opp

This comment has been minimized.

Copy link
Owner

@phil-opp phil-opp commented Feb 2, 2017

Hmm, that's strange. Are you compiling with optimizations?

@Dentosal

This comment has been minimized.

Copy link
Contributor Author

@Dentosal Dentosal commented Feb 2, 2017

Yes. I'm compiling with --release. I somehow thought you are doing so too, but apparently that is not the case.

BTW, I also had same issue with paging, because code has no side-effects, and some of it was getting optimized away.

@phil-opp

This comment has been minimized.

Copy link
Owner

@phil-opp phil-opp commented Feb 2, 2017

Yes. I'm compiling with --release. I somehow thought you are doing so too, but apparently that is not the case.

I don't use --release at the moment, since it makes debugging more difficult and the kernel is still fast enough without it. The problem with --release is that the compiler is clever. For example, the compiler might turn your modified stack_overflow function into loop { print!("."); } (through tail call optimization), which no longer causes a stack overflow. A do-not-optimize attribute for functions would be great for this case…

However, the code should also work with --release, of course. I'll try to find a different way to cause stack overflows, that also works with optimizations.

BTW, I also had same issue with paging, because code has no side-effects, and some of it was getting optimized away.

That sounds really bad 😟.


Thanks a lot for reporting these issues! I'm quite busy at the moment, but I'll try to investigate and fix them as soon as I have some time again.

@phil-opp phil-opp self-assigned this Feb 2, 2017
@phil-opp phil-opp added the bug label Feb 2, 2017
@bjorn3

This comment has been minimized.

Copy link
Contributor

@bjorn3 bjorn3 commented Feb 2, 2017

Does the following work?

#[inline(never)]
fn stack_overflow(){
    print!(".");
    stack_overflow();
    print!(".");
    stack_overflow();
}

Or

#[inline(never)]
fn a(){
    b();
}
#[inline(never)]
fn b(){
    print(".");
    a();
}
@phil-opp

This comment has been minimized.

Copy link
Owner

@phil-opp phil-opp commented Feb 15, 2017

So there are two issues here:

  1. Tail call optimization might turn an endless recursion into a loop. I think the proper way to avoid this is to add an inline(never) annotation.
  2. The optimizer removes endless loops without side effects. This is a known bug caused by undefined behavior in LLVM: rust-lang/rust#28728
@phil-opp phil-opp removed the bug label Jul 11, 2018
@samgiles

This comment has been minimized.

Copy link

@samgiles samgiles commented Nov 8, 2019

My test for this works with --release optimisations on:

#[allow(unconditional_recursion)]
fn stack_overflow(n: u64) -> u64 {
    // We need to indicate to the compiler that it should disable optimisations by black_boxing `n` 
   stack_overflow(black_box(n + 1))
}

/// lifted from: https://doc.rust-lang.org/src/core/hint.rs.html#111
fn black_box<T>(dummy: T) -> T {
    unsafe {
        asm!("": : "r"(&dummy));
        return dummy;
    }
}
@64

This comment has been minimized.

Copy link

@64 64 commented Nov 11, 2019

Can you elaborate on the paging issues? It's a known issue that all of the accesses to the page tables should be volatile and ideally non-tearing-guaranteed (probably through inline asm), but I haven't heard of any problems with these things in practice.

woops, didn't see the original post date - ignore (although an answer would still be nice 😄)

@Dentosal

This comment has been minimized.

Copy link
Contributor Author

@Dentosal Dentosal commented Nov 11, 2019

@64 Unfortunately, I never really solved the issues. I didn't know how to use bochs debugger then. My paging code was a huge mess anyways, so I have rewritten it twice since then. I'm now using PageTable struct from x86_64 crate with my own custom mapper, and it seems to work. No idea why, though. I access the page tables like this:

let mut table: &mut PageTable = unsafe { &mut *virt_addr.as_mut_ptr() };

And I make sure the reference is dropped before I flush changes to TLB.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants
You can’t perform that action at this time.