You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Rust裸指针可访问已超出作用域结构体的函数与部分字段信息?

关于Rust裸指针Use-After-Free未触发Panic的解析

首先要明确:Use-After-Free属于未定义行为(UB),Rust不会主动检测或触发panic,程序可能表现出任何行为(包括看似正常运行),这完全取决于内存布局和后续的内存操作。

1. 为什么test()方法能成功调用

Rust的方法调用本质是静态分发的函数调用:(*p).test()等价于Foo::test(&*p)。由于test方法没有访问self的任何字段,只是执行println!("test"),即使传递的&self是指向已释放内存的无效引用,只要该引用的地址没有被覆盖,函数就能正常执行——毕竟它不需要读取实例的任何数据。

2. 为什么data长度固定但内容随机

Vec在内存中由三个字段组成:指向堆内存的指针、元素长度、容量。当Foo实例被释放时:

  • Vec的堆内存(存储u8数据的区域)会被标记为可用,但栈上Foo实例的内存(包含Vec的三个字段)只是被标记为可复用,不会立即被清零或覆盖。
  • 你读取到的长度是栈上残留的旧值,所以每次长度都和输入一致;而堆内存已经被释放,后续可能被其他操作覆盖,所以内容是随机的。

3. 如何让Use-After-Free的问题显现

要触发崩溃或异常,需要让已释放的内存被覆盖,或者执行依赖有效内存的操作:

  • 修改test方法访问实例字段:比如让test打印self.data.len(),当栈上的Vec结构被后续操作覆盖后,读取无效的长度值可能触发崩溃。
  • 在释放后立即分配大量内存:通过分配大的Vec或其他数据结构,覆盖栈或堆上的旧内存区域,再访问裸指针:
    fn main() {
        let mut p: *const Foo = std::ptr::null();
        match 1 {
            1 => {
                let foo = Foo::new(&[1,2,3]);
                p = foo.as_ptr();
            }
            _ => {}
        }
        // 分配大量内存,覆盖旧内存
        let _big_vec = vec![0u8; 4096];
        unsafe {
            (*p).test(); // 此时可能触发崩溃或行为异常
            println!("{:?}", (*p).data);
        }
    }
    
  • 使用MIRI检测未定义行为:MIRI是Rust的官方未定义行为检测器,能在编译时精准定位Use-After-Free问题,比等待运行时崩溃更可靠:
    cargo miri run
    
    运行后Miri会直接报错,指出你正在访问已释放的内存。

内容的提问来源于stack exchange,提问作者gfzum

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 19:23:24