$ RUSTFLAGS="-Z codegen-backend=cranelift" cargo build
Compiling project v0.1.0 (/tmp/project)
Finished dev [unoptimized + debuginfo] target(s) in 0.23s
$ ./target/debug/project
$ echo $?
0
$ valgrind ./target/debug/project
==9258== Memcheck, a memory error detector
==9258== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==9258== Using Valgrind-3.21.0 and LibVEX; rerun with -h for copyright info
==9258== Command: ./target/debug/project
==9258==
==9258== Invalid write of size 4
==9258== at 0x10F5FF: project::main (main.rs:1)
==9258== by 0x10F68F: core::ops::function::FnOnce::call_once (function.rs:250)
==9258== by 0x10F673: std::sys_common::backtrace::__rust_begin_short_backtrace (backtrace.rs:154)
==9258== by 0x10F720: std::rt::lang_start::{{closure}} (rt.rs:167)
==9258== by 0x1260A6: call_once<(), (dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (function.rs:284)
==9258== by 0x1260A6: do_call<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panicking.rs:552)
==9258== by 0x1260A6: try<i32, &(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (panicking.rs:516)
==9258== by 0x1260A6: catch_unwind<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panic.rs:142)
==9258== by 0x1260A6: {closure#2} (rt.rs:148)
==9258== by 0x1260A6: do_call<std::rt::lang_start_internal::{closure_env#2}, isize> (panicking.rs:552)
==9258== by 0x1260A6: try<isize, std::rt::lang_start_internal::{closure_env#2}> (panicking.rs:516)
==9258== by 0x1260A6: catch_unwind<std::rt::lang_start_internal::{closure_env#2}, isize> (panic.rs:142)
==9258== by 0x1260A6: std::rt::lang_start_internal (rt.rs:148)
==9258== by 0x10F6F5: std::rt::lang_start (rt.rs:166)
==9258== by 0x10F5F5: main (in /tmp/project/target/debug/project)
==9258== Address 0x1ffeffe5d0 is on thread 1's stack
==9258== 4096 bytes below stack pointer
==9258==
==9258== Invalid write of size 4
==9258== at 0x10F606: project::main (main.rs:1)
==9258== by 0x10F68F: core::ops::function::FnOnce::call_once (function.rs:250)
==9258== by 0x10F673: std::sys_common::backtrace::__rust_begin_short_backtrace (backtrace.rs:154)
==9258== by 0x10F720: std::rt::lang_start::{{closure}} (rt.rs:167)
==9258== by 0x1260A6: call_once<(), (dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (function.rs:284)
==9258== by 0x1260A6: do_call<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panicking.rs:552)
==9258== by 0x1260A6: try<i32, &(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (panicking.rs:516)
==9258== by 0x1260A6: catch_unwind<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panic.rs:142)
==9258== by 0x1260A6: {closure#2} (rt.rs:148)
==9258== by 0x1260A6: do_call<std::rt::lang_start_internal::{closure_env#2}, isize> (panicking.rs:552)
==9258== by 0x1260A6: try<isize, std::rt::lang_start_internal::{closure_env#2}> (panicking.rs:516)
==9258== by 0x1260A6: catch_unwind<std::rt::lang_start_internal::{closure_env#2}, isize> (panic.rs:142)
==9258== by 0x1260A6: std::rt::lang_start_internal (rt.rs:148)
==9258== by 0x10F6F5: std::rt::lang_start (rt.rs:166)
==9258== by 0x10F5F5: main (in /tmp/project/target/debug/project)
==9258== Address 0x1ffeffd5d0 is on thread 1's stack
==9258== 8192 bytes below stack pointer
==9258==
==9258== Invalid write of size 4
==9258== at 0x10F60D: project::main (main.rs:1)
==9258== by 0x10F68F: core::ops::function::FnOnce::call_once (function.rs:250)
==9258== by 0x10F673: std::sys_common::backtrace::__rust_begin_short_backtrace (backtrace.rs:154)
==9258== by 0x10F720: std::rt::lang_start::{{closure}} (rt.rs:167)
==9258== by 0x1260A6: call_once<(), (dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (function.rs:284)
==9258== by 0x1260A6: do_call<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panicking.rs:552)
==9258== by 0x1260A6: try<i32, &(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (panicking.rs:516)
==9258== by 0x1260A6: catch_unwind<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panic.rs:142)
==9258== by 0x1260A6: {closure#2} (rt.rs:148)
==9258== by 0x1260A6: do_call<std::rt::lang_start_internal::{closure_env#2}, isize> (panicking.rs:552)
==9258== by 0x1260A6: try<isize, std::rt::lang_start_internal::{closure_env#2}> (panicking.rs:516)
==9258== by 0x1260A6: catch_unwind<std::rt::lang_start_internal::{closure_env#2}, isize> (panic.rs:142)
==9258== by 0x1260A6: std::rt::lang_start_internal (rt.rs:148)
==9258== by 0x10F6F5: std::rt::lang_start (rt.rs:166)
==9258== by 0x10F5F5: main (in /tmp/project/target/debug/project)
==9258== Address 0x1ffeffc5d0 is not stack'd, malloc'd or (recently) free'd
==9258==
==9258==
==9258== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==9258== Access not within mapped region at address 0x1FFEFFC5D0
==9258== at 0x10F60D: project::main (main.rs:1)
==9258== by 0x10F68F: core::ops::function::FnOnce::call_once (function.rs:250)
==9258== by 0x10F673: std::sys_common::backtrace::__rust_begin_short_backtrace (backtrace.rs:154)
==9258== by 0x10F720: std::rt::lang_start::{{closure}} (rt.rs:167)
==9258== by 0x1260A6: call_once<(), (dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (function.rs:284)
==9258== by 0x1260A6: do_call<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panicking.rs:552)
==9258== by 0x1260A6: try<i32, &(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (panicking.rs:516)
==9258== by 0x1260A6: catch_unwind<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panic.rs:142)
==9258== by 0x1260A6: {closure#2} (rt.rs:148)
==9258== by 0x1260A6: do_call<std::rt::lang_start_internal::{closure_env#2}, isize> (panicking.rs:552)
==9258== by 0x1260A6: try<isize, std::rt::lang_start_internal::{closure_env#2}> (panicking.rs:516)
==9258== by 0x1260A6: catch_unwind<std::rt::lang_start_internal::{closure_env#2}, isize> (panic.rs:142)
==9258== by 0x1260A6: std::rt::lang_start_internal (rt.rs:148)
==9258== by 0x10F6F5: std::rt::lang_start (rt.rs:166)
==9258== by 0x10F5F5: main (in /tmp/project/target/debug/project)
==9258== If you believe this happened as a result of a stack
==9258== overflow in your program's main thread (unlikely but
==9258== possible), you can try to increase the size of the
==9258== main thread stack using the --main-stacksize= flag.
==9258== The main thread stack size used in this run was 8388608.
==9258==
==9258== HEAP SUMMARY:
==9258== in use at exit: 85 bytes in 3 blocks
==9258== total heap usage: 10 allocs, 7 frees, 2,157 bytes allocated
==9258==
==9258== LEAK SUMMARY:
==9258== definitely lost: 0 bytes in 0 blocks
==9258== indirectly lost: 0 bytes in 0 blocks
==9258== possibly lost: 0 bytes in 0 blocks
==9258== still reachable: 85 bytes in 3 blocks
==9258== suppressed: 0 bytes in 0 blocks
==9258== Rerun with --leak-check=full to see details of leaked memory
==9258==
==9258== For lists of detected and suppressed errors, rerun with: -s
==9258== ERROR SUMMARY: 4 errors from 3 contexts (suppressed: 0 from 0)
[1] 9258 segmentation fault (core dumped) valgrind ./target/debug/project
I tried
rustc_codegen_cranelifton some of my projects, and found that even though the binaries appeared to run normally, they produced errors and segfaults invalgrind. Looking at the disassembly, it appeared that valgrind doesn’t like the way Cranelift performs stack probing..clifTest CaseThis is the most minimal Rust code that I came up with:
which generates the following
.cliffile:output file
main.clif/_ZN4main4main17hf30ba8656d3abcbbE.unopt.clifgenerated by
rustc -Z codegen-backend=cranelift src/main.rs --emit=llvm-irSteps to Reproduce
Expected Results
When run in valgrind, this program should not produce any errors.
Actual Results
valgrind complains about out-of-bounds stack writes and then lets the program segfault on a write to an unmapped address:
valgrind output
Versions and Environment
valgrind 3.21.0 (I realise this is not the current version, but I didn’t find anything related to stacks in the changelog for 3.22.0 in case this is a false positive in valgrind.)
Extra Info
This is the disassembly of
main:valgrind doesn’t like that the stack is written to before the stack pointer is moved.
Inline stack probing was introduced in #4747. Only unrolled stack probing is problematic, the loop is okay for valgrind as the stack pointer is moved before the write.