Skip to content

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);
  } 
}