导致段错误的 Rust 代码示例是什么?

What is an example of Rust code that causes a segfault?

我用谷歌搜索了一些 Rust 中的段错误示例,但 none 现在崩溃了。 Rust 现在能够防止所有段错误吗?是否有可能导致段错误的简单演示?

如果允许unsafe代码,则:

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

结果:

   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} "$@"

playground.

上看到

任何会触发段错误的情况都需要在某个时候调用未定义的行为。允许编译器优化代码或以其他方式利用未定义行为永远不会发生的事实,因此很难保证某些代码会出现段错误。编译器完全有权在不触发段错误的情况下制作上述程序运行。

例如,上面的代码在 release 模式下编译时会导致“非法指令”。


如果 unsafe 代码是不允许的,请参阅 了解 Rust 如何保证它不会发生,只要它的内存安全不变量不被违反(这只能发生在 unsafe代码)。

如果可以避免,请不要使用不安全的代码。

严格来说,总是有可能欺骗程序认为它有段错误,因为这是 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

这并不是您问题的真正答案,因为这不是“真正的”分段错误,但从字面上理解这个问题——Rust 程序仍然可以以“分段错误”错误结束,这是一个可靠的案例触发它。

如果您更普遍地寻找会转储核心的东西,而不是专门导致段错误,还有另一种选择是使编译器发出 UD2 指令或等效指令。有一些东西可以产生这个:

  • 没有任何副作用的空循环:

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

    Playground.

    这不再产生 UB。

  • 正在尝试创建 never (!) type

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

    Playground.

  • 或者也尝试使用(在本例中匹配)没有变体的枚举:

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

    Playground.

  • 最后,您可以作弊并直接强制它生成 UD2:

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

    Playground

    或使用llvm_asm! instead of asm!

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

    Playground.

NULL 指针在 C 和 Rust 中都可能导致段错误。

union Foo<'a>{
    a:i32,
    s:&'a str,
}
fn main() {
    let mut a = Foo{s:"fghgf"};
    a.a = 0;
    unsafe {
        print!("{:?}", a.s);
    }
}