8

I Googled some segfault examples in Rust, but none of crash now. Is Rust able to prevent all segfaults now? Is there a simple demo that can cause a segfault?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
潇洒张
  • 273
  • 2
  • 9

6 Answers6

10

If unsafe code is allowed, then:

fn main() {
    unsafe { std::ptr::null_mut::<i32>().write(42) };
}

results in:

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 1.37s
     Running `target/debug/playground`
timeout: the monitored command dumped core
/playground/tools/entrypoint.sh: line 11:     7 Segmentation fault      timeout --signal=KILL ${timeout} "$@"

as seen on the playground.


Any situation that would trigger a segfault would require invoking undefined behavior at some point. The compiler is allowed to optimize out code or otherwise exploit the fact that undefined behavior should never occur, so it's very hard to guarantee that some code will segfault. The compiler is well within its rights to make the above program run without triggering a segfault.

As an example, the code above when compiled in release mode results in an "Illegal instruction" instead.


If unsafe code is not allowed, see How does Rust guarantee memory safety and prevent segfaults? for how Rust can guarantee it doesn't happen as long as its memory safety invariants aren't violated (which could only happen in unsafe code).

Don't use unsafe code if you can avoid it.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • 1
    @潇洒张 Additionally, if you're only looking for something that will dump core, then [this](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=0e4c0f9e27daff3d47bd740b82f9cc39) would suit the job and probably produce the same result for the foreseeable future. – Optimistic Peach Jul 11 '20 at 06:18
  • @OptimisticPeach can you add that as an Answer? – ti7 Jul 20 '20 at 15:22
  • 1
    @ti7 Yup, there you go! – Optimistic Peach Jul 20 '20 at 18:21
8

Strictly speaking, it is always possible to trick a program into thinking that it had a segmentation fault, since this is a signal sent by the OS:

use libc::kill;
use std::process;

fn main() {
    unsafe {
        // First SIGSEGV will be consumed by Rust runtime
        // (see https://users.rust-lang.org/t/is-sigsegv-handled-by-rust-runtime/45680)...
        kill(process::id() as i32, libc::SIGSEGV);
        // ...but the second will crash the program, as expected
        kill(process::id() as i32, libc::SIGSEGV);
    }
}

Playground

This is not really an answer to your question, since that's not a "real" segmentation fault, but taking the question literally - Rust program can still end with a "segmentation fault" error, and here's a case which reliably triggers it.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Cerberus
  • 8,879
  • 1
  • 25
  • 40
  • 1
    If anyone is seeking an intentional segfault, this is probably the safer option as well, since segfaults being UB can be inherently dangerous due to unknowable reasons. (And don't ask me why someone would WANT it, but anyways \*copies code\*) – Kobato Apr 11 '21 at 02:13
  • 1
    Actually, using the nix crate seems even better: `signal::raise(signal::Signal::SIGSEGV).unwrap();` (I believe Unix only though, sadly) – Kobato Apr 11 '21 at 13:11
3

If you're looking more generally for something that will dump core, and not specifically cause a segfault, there is another option which is to cause the compiler to emit an UD2 instruction or equivalent. There's a few things which can produce this:

  • An empty loop without any side effects is UB because of LLVM optimizations:

    fn main() {
         (|| loop {})()
    }
    

    Playground.

    This no longer produces UB.

  • Trying to create the never (!) type.

    #![feature(never_type)]
    union Erroneous {
         a: (),
         b: !,
    }
    
    fn main() {
         unsafe { Erroneous { a: () }.b }
    }
    

    Playground.

  • Or also trying to use (in this case match on) an enum with no variants:

    #[derive(Clone, Copy)]
    enum Uninhabited {}
    
    union Erroneous {
         a: (),
         b: Uninhabited,
    }
    
    fn main() {
         match unsafe { Erroneous { a: () }.b } {
             // Nothing to match on.
         }
    }
    

    Playground.

  • And last, you can cheat and just force it to produce a UD2 directly:

    #![feature(asm)]
    fn main() {
         unsafe {
             asm! {
                 "ud2"
             }
         };
    }
    

    Playground

    Or using llvm_asm! instead of asm!

    #![feature(llvm_asm)]
    fn main() {
         unsafe {
             llvm_asm! {
                 "ud2"
             }
         };
    }
    

    Playground.

Optimistic Peach
  • 3,862
  • 2
  • 18
  • 29
1

In safe Rust, you can deliberately mess up the memory of your own process via OS facilities.

use std::io::Write;
use std::io::Seek;

fn main() {
    let x = 42;
    let y = &x;

    // Delete the next few lines and everything is good.
    let mut f = std::fs::OpenOptions::new()
        .write(true)
        .open("/proc/self/mem").expect("welp");
    // Turn y into a nullptr
    f.seek(std::io::SeekFrom::Start(&y as *const _ as u64)).expect("oof");
    f.write(&0usize.to_ne_bytes()).expect("darn");

    println!("{y}");
}

Playground

While this has some duuuuh-factor, the general technique has made some waves. It was even considered whether this is an unsoundness, but ultimately mostly rejected. (Argument being that Rust doesn't protect against machine weirdnesses, be it bitflips or the OS messing with process memory.)


Another no-unsafe SIGSEGV is by stack overflow, as mentioned (but not explained) by @guest_703. A different example:

fn main() {
  main();
}

Playground

Note that

  • This is not considered observable behavior by rustc and may be optimized out in release mode.
  • While this is a SIGSEGV in safe Rust, it's not an unsoundness / violation of memory safety, because it can never lead to UB / invalid access to allocated memory.
  • The stacker crate may be used to avoid stack overflows.
Caesar
  • 6,733
  • 4
  • 38
  • 44
0

NULL pointer can cause segfault in both C and Rust.

union Foo<'a>{
    a:i32,
    s:&'a str,
}
fn main() {
    let mut a = Foo{s:"fghgf"};
    a.a = 0;
    unsafe {
        print!("{:?}", a.s);
    }
}
ianfun
  • 411
  • 5
  • 10
  • 1
    This is unsafe code, which is fair enough since the OP didn't specify but he probably means is a segfault possible in safe rust code. – Aaron May 13 '22 at 12:08
0

fn main() { let arr = [5; 1000000000] }

  • It'd be nice if you could add a little bit of text why this causes a SEGV. Also, you're lacking a `;`. (Otherwise: good point. Maybe also add an example with infinite recursion?) – Caesar Jun 07 '23 at 06:45