1 个不稳定版本
0.1.0 | 2023年9月20日 |
---|
#517 in Unix APIs
62KB
1K SLoC
hw-exception
这个Rust库处理由硬件异常触发的POSIX信号。这些信号包括
SIGILL
SIGFPE
SIGSEGV
SIGBUS
SIGTRAP
触发这些信号的硬件异常的例子包括
- 非法指令
- 一般保护故障
- 除以零错误
- 浮点异常
- 页面错误
- 一般保护故障
- 机器检查异常(例如,由于ECC内存的双比特错误而引发)
- 硬件断点
通常,接收到这些信号表明要么是硬件故障,要么是在安全的Rust代码中不可能存在的一些类型的错误。当它们意外接收时,唯一合理的做法是终止进程并转储核心,这正是通常会发生的情况。然而,存在许多预期这些信号并可能恢复的场景。以下是一些例子
- 停止并复制垃圾收集器。某些垃圾收集技术通常会触发段错误。信号处理器可以将一个有效的页面映射到故障地址,然后执行可以从中恢复。(考虑
userfaultfd
crate作为此用途和类似场景的替代方案。) - 与不受信任的同伴共享内存。共享内存段的所有写者可能会进行各种不友好的操作,例如意外截断它,这将导致访问该段的进程接收到
SIGBUS
,而它们无法通过运行TOCTOU问题来保护自己。此类行为的受害者可以捕获信号并跳转到恢复点。(考虑memfd
crate作为避免此类复杂性的替代方案。) - 复杂的数值操作。有时,让除以零或浮点异常发生比检查可能触发它的每个操作更有效率。
- 鲁棒的存储层。随着磁盘或内存大小接近无穷大,硬件错误发生的概率接近1。捕获机器检查异常可以通过切换到冗余存储或容忍少量数据丢失来鲁棒地处理此类故障。
- 调试器,在遇到它们设置的断点时将收到
SIGTRAP
。
硬件异常通常以三种方式之一进行处理;这个库在某种程度上支持所有这些方式。它们是
- 修补并继续:在信号处理器中修复问题,然后从中返回以重新执行异常指令。例如,通过映射一个有效的页面来纠正段错误。
- 捕获并恢复:使用
setjmp
存储一个恢复点,然后从信号处理器中longjmp
回到它。 - 尖叫并死亡:根本不尝试从异常中恢复;只需使用信号处理器在终止前记录一些诊断信息。
示例
以下示例通过取消引用空指针触发段错误,捕获并从中恢复,然后打印一个回溯,显示段错误发生的位置。
use hw_exception::*;
use std::backtrace::Backtrace;
fn main() {
unsafe {
// Register a hook for SIGSEGV, which captures and throws a backtrace.
register_hook(&[Signo::SIGSEGV], |e| {
let bt = Backtrace::force_capture();
throw((e, bt))
});
}
// Dereference a null pointer from within a `catch` block. Using `read_volatile`
// prevents this from being UB.
let result = catch(|| unsafe {
std::ptr::null::<usize>().read_volatile()
});
// Assert that this block resulted in an exception, and extract it.
let e = result.expect_err("dereferencing a null pointer should have segfaulted, but gave");
// Extract and print the backtrace
let bt : &Backtrace = e
.additional()
.expect("thrown exception info should have included additional data")
.downcast_ref()
.expect("additional data should have been a `Backtrace`");
println!("{}", bt);
}
文档
请参阅docs.rs上的API文档。
许可证
本项目许可在Apache License 2.0下,带有LLVM异常。除非您明确说明,否则您提交给hw-exception
的任何有意贡献,均按Apache 2.0许可并带有LLVM异常,不附加任何额外条款或条件。
依赖项
~225KB