G.SAF.MEM.02 Unsafe 模式下,使用指针及内存地址时应确保有效性
【级别】 要求
【描述】
在 Unsafe Rust 中,通过裸指针方式申请分配内存应注意:
- 指针解引用前应确保指向的内存已初始化
访问未初始化内存会导致未定义行为。
- FFI函数调用返回的指针应先检查是否为空再使用
解引用空指针会导致程序崩溃。
- 不应将局部变量的栈地址返回到作用域之外
栈内存会在函数返回后被回收,访问会导致未定义行为。
- 指针指向的内存释放后禁止继续访问
指针指向的内存释放后,继续通过指针访问会导致未定义行为。
【反例】
禁止使用未初始化的指针
Rust
fn main() {
let foo_ptr: *mut Foo = std::ptr::null_mut();
// 不符合:禁止使用未初始化的指针
unsafe { println!("{:?}", (*foo_ptr).ptr); }
}
// 模拟一个外部 c-struct
struct Foo<'a> {
ptr: &'a i32,
}
【正例】
禁止使用未初始化的指针
Rust
use std::mem::MaybeUninit;
fn main() {
// 符合:建议的外部数据结构初始化方式
let foo: Foo = unsafe {
let mut foo = MaybeUninit::uninit();
initialize(foo.as_mut_ptr());
foo.assume_init()
};
println!("{:?}", foo.ptr);
}
// 模拟一个外部 c-struct
struct Foo<'a> { ptr: &'a i32, }
// 模拟一个外部 c 函数
fn initialize(p: *mut Foo) {
unsafe { (*p).ptr = &0 };
}
【反例】
禁止将局部变量的地址返回到作用域之外
Rust
fn main() {
let p = return_stack_address();
unsafe { println!("{}", *p); }
}
fn return_stack_address() -> *const i32 {
let value = 123_i32;
// 不符合:禁止将局部变量的地址返回到作用域之外
&value as *const i32
}
【反例】 禁止释放指针后继续使用
Rust
const BUF_SIZE: usize = 100;
unsafe {
let buf = malloc(BUF_SIZE) as *mut libc::c_char;
// 操作指针 , 写入数据
free(buf as *mut libc::c_void);
// 不符合:free 内存后数据还在, `buf` 变成悬挂指针且不为空。
// 因此仍然会解引用指针
if !buf.is_null() {
println!("{:?}", *buf);
}
}
【正例】
Rust
const BUF_SIZE: usize = 100;
unsafe {
let mut buf = malloc(BUF_SIZE) as *mut libc::c_char;
// 操作指针 , 写入数据
// 符合:释放内存后将指针设置为 null
free(buf as *mut c_void);
buf = std::ptr::null_mut();
// 符合: `ptr` 为空,判断式不成立所以不会解引用。
if !buf.is_null() {
println!("{:?}", *buf);
}
}