Description
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