Skip to content

__rust_probestack needs CFI annotations to assist in backtracing #304

Closed
@da-x

Description

@da-x

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions