4

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
  • 326,504
  • 69
  • 892
  • 1,159
潇洒张
  • 203
  • 2
  • 7

4 Answers4

6

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
  • 326,504
  • 69
  • 892
  • 1,159
kmdreko
  • 25,172
  • 5
  • 32
  • 61
  • 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
5

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
  • 326,504
  • 69
  • 892
  • 1,159
Cerberus
  • 6,699
  • 1
  • 23
  • 37
  • 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
  • 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,239
  • 2
  • 17
  • 27
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
  • 117
  • 2
  • 8
  • 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