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

__rust_probestack needs CFI annotations to assist in backtracing #304

Closed
da-x opened this issue Jul 19, 2019 · 2 comments
Closed

__rust_probestack needs CFI annotations to assist in backtracing #304

da-x opened this issue Jul 19, 2019 · 2 comments

Comments

@da-x
Copy link
Member

da-x commented Jul 19, 2019

In a stack overflow situations, under Rust 1.36, gdb fails to backtrace. Also, various other backtracer mechanisms fail to work. The reason is that __rust_probestack meddles with the stack register, and gdb is missing the required information in order to find the stack frame which triggered the overflow. Even with -C debuginfo=1 this still happens, because the relevant assembly code is manually written without the required annotations.

Here how it looks like:

>>> bt
#0  0x0000555555576f73 in __rust_probestack () at /cargo/registry/src/github.com-1ecc6299db9ec823/compiler_builtins-0.1.14/src/probestack.rs:55
Backtrace stopped: Cannot access memory at address 0x7fffff7fee50

Example program test.rs:

fn main() {
    let arr : [u8; 0xf000000] = [0x1; 0xf000000];
    let mut sum : u64 = 0;

    for i in arr.iter() {
        sum += *i as u64;
    }

    println!("{}", sum);
}

Observing the assembly code, it is possible to figure out based on $rdi and $rsp what should be the offset to add to $rsp in order to bring gdb to the correct analysis.

>>> set $rsp = $rsp + 0x7fe000
>>> bt
#0  0x0000555555576f83 in __rust_probestack () at /cargo/registry/src/github.com-1ecc6299db9ec823/compiler_builtins-0.1.14/src/probestack.rs:55
#1  0x000055555555835a in test::main () at a.rs:1

One workaround that can be implemented by various backtracers (the internal, and the backtrace crate) is automatic hand-coded compensation, by figuring out the state of the __rust_probestack function.

Proof-of-concept (assuming ctx captured CPU state):

// `Pseudo`-code

#[cfg(target_arch = "x86_64")]
if symbol_name == "__rust_probestack" {
    let ptr = ctx.rip as *const u8;
    let back = unsafe { std::slice::from_raw_parts(ptr.offset(-6), 6) };
    let mut array = [0u8; 6];
    array.copy_from_slice(&back);

    let matched = {
        if array == [0x81, 0xec, 0x00, 0x10, 0x00, 0x00] {
            let diff = 0x1000 + (ctx.rax - ctx.r11);
            ctx.rsp += diff;
            true
        } else if array == [0x00, 0x77, 0xe4, 0x4c, 0x29, 0xdc] {
            ctx.rsp += ctx.rax;
            true
        } else {
            false
        }
    };

    if matched { // Unwind
        ctx.rip = unsafe { *(ctx.rsp as *const u64) };
    }
}

backtrace_from(&ctx);

While I am against this particular workaround in the general sense, one might find it useful as a stop-gap, at least until compiler-builtins is fixed.

Related issue: rust-lang/rust#51405

@alexcrichton
Copy link
Member

This may be fixable by having standard management of the base pointer, but otherwise I'm not sure how we'd do this with LLVM's inline assembly so we could have a raw assembly file compiled in if necessary.

@alexcrichton
Copy link
Member

Fixed now!

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

Successfully merging a pull request may close this issue.

2 participants