Rust代码导致segfault的例子是什么?

toiithl6  于 2023-06-23  发布在  其他
关注(0)|答案(6)|浏览(106)

我在Rust中搜索了一些segfault示例,但现在没有崩溃。Rust现在能防止所有segfaults吗?是否有一个简单的演示可以导致segfault?

d5vmydt9

d5vmydt91#

如果允许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上所见。
任何会触发segfault的情况都需要在某个点调用未定义的行为。编译器被允许优化代码或利用未定义行为永远不会发生的事实,所以很难保证某些代码会segfault。编译器完全有权在不触发segfault的情况下运行上述程序。
作为示例,以上代码在 release 模式下编译时反而导致“非法指令”。
如果不允许使用unsafe代码,请参阅How does Rust guarantee memory safety and prevent segfaults?,了解Rust如何保证只要不违反其内存安全不变量(这只可能发生在unsafe代码中),就不会发生这种情况。
不要使用不安全的代码,如果你能避免它。

pcrecxhr

pcrecxhr2#

严格地说,总是可以欺骗程序认为它有分段错误,因为这是操作系统发送的信号:

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程序仍然可以以“分段错误”错误结束,这里有一个可靠触发它的案例。

eit6fx6z

eit6fx6z3#

如果你正在寻找一个更普遍的东西,将转储核心,而不是专门导致segfault,还有另一个选项,那就是导致编译器发出UD2指令或等效指令。有几个因素可以产生这种现象:

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

Playground
这不再产生UB。

#![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!而不是asm!

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

Playground

ne5o7dgx

ne5o7dgx4#

在safe Rust中,您可以通过OS设施故意搞乱您自己进程的内存。

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
虽然这有一些duuuuh因素,但一般技术有made some waves。甚至有人考虑这是否是一个不健全的,但最终大多被否决。(理由是Rust不能防止机器的怪异,无论是bitflips还是操作系统扰乱进程内存。
另一个不安全的SIGSEGV是堆栈溢出,正如@guest_703所提到的(但没有解释)。另一个例子:

fn main() {
  main();
}

Playground
注意

  • 这不被认为是rustc可观察到的行为,并且可以在释放模式中优化。
  • 虽然这是safe Rust中的SIGSEGV,但它并不是内存安全的不健全/违反,因为它永远不会导致对分配内存的UB /无效访问。
  • stacker crate可用于避免堆栈溢出。
bqujaahr

bqujaahr5#

NULL指针在C和Rust中都可能导致segfault。

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

jaql4c8m6#

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

相关问题