Rust Undifined Behavior
小心UB,使用unsafe的时候多看接口文档
1. Memory
1.1 uninitialized memory
#![allow(invalid_value)]
use std::mem::{self, MaybeUninit};
let x: &i32 = unsafe { mem::zeroed() }; // undefined behavior!
// The equivalent code with `MaybeUninit<&i32>`:
let x: &i32 = unsafe { MaybeUninit::zeroed().assume_init() }; // undefined behavior!
上面即使不使用这个引用,也是UB
类似的,下面也是UB
let x: i32 = unsafe { mem::uninitialized() }; // undefined behavior!
let b: bool = unsafe { mem::uninitialized() };
关于这个UB,有讨论 Uninitialized memory varying from read to read
1.2 unchecked index
调用slice方法get_unchecked()
,需要调用者保证不越界,如果越界则是UB,即使不读返回值。
1.3 alloc
全局alloc
不能申请size为0的内存 (所以Box不会为ZST真的申请内存)。 但rust中用指针读写ZST是可以的,但要求指针不为0且对齐。
1.4 misaligned
读写未对齐的指针是UB
1.5 offset
pub const unsafe fn offset(self, count: isize) -> *const T
Rust的offset比较严格,违反以下3点任何一个就是UB,
- Both the starting and resulting pointer must be either in bounds or one byte past the end of the same allocated object.
- The computed offset, in bytes, cannot overflow an
isize
. - The offset being in bounds cannot rely on “wrapping around” the address space. That is, the infinite-precision sum, in bytes must fit in a usize.
所以并不是简单算一个新地址就好,这些限制的好处是能让编译器做更激进的优化。
2. Error Handling
2.1 unwind
无论是从别的语言unwind进rust,还是rust unwind进别的语言,都是UB,所以要在FFI时注意异常的边界,要抓住panic可以用catch_unwind()
方法。
3. concurrency
3.1 data race
data race在Rust中算作UB,所以要合理控制并发