P.UNS.SAS.05 手动实现 auto trait 时要充分考虑其安全性

【描述】

所谓 auto trait 是指 Safe Rust中由编译器自动实现的 trait,比如 Send/Sync 。在 Unsafe Rust中就需要手动实现这俩 trait 了。

所以,在手动实现的时候要充分考虑其安全性。

【正例】

Rust futures 库中发现的问题,错误的手工 Send/Sync 实现 破坏了线程安全保证。

受影响的版本中,MappedMutexGuardSend/Sync 实现只考虑了 T 上的差异,而 MappedMutexGuard 则取消了对 U 的引用。

MutexGuard::map() 中使用的闭包返回与 T 无关的 U 时,这可能导致安全 Rust 代码中的数据竞争。

这个问题通过修正 Send/Sync 的实现,以及在 MappedMutexGuard 类型中添加一个 PhantomData<&'a mut U> 标记来告诉编译器,这个防护也是在 U 之上。


#![allow(unused)]
fn main() {
// CVE-2020-35905: incorrect uses of Send/Sync on Rust's futures
pub struct MappedMutexGuard<'a, T: ?Sized, U: ?Sized> {
    mutex: &'a Mutex<T>,
    value: *mut U,
    _marker: PhantomData<&'a mut U>, // + 修复代码
}

impl<'a, T: ?Sized> MutexGuard<'a, T> {
    pub fn map<U: ?Sized, F>(this: Self, f: F)
        -> MappedMutexGuard<'a, T, U>
        where F: FnOnce(&mut T) -> &mut U {
            let mutex = this.mutex;
            let value = f(unsafe { &mut *this.mutex.value.get() });
                mem::forget(this);
                // MappedMutexGuard { mutex, value }
                MappedMutexGuard { mutex, value, _marker: PhantomData } //  + 修复代码
    }
}

// unsafe impl<T: ?Sized + Send, U: ?Sized> Send
unsafe impl<T: ?Sized + Send, U: ?Sized + Send> Send // + 修复代码
for MappedMutexGuard<'_, T, U> {}
//unsafe impl<T: ?Sized + Sync, U: ?Sized> Sync
unsafe impl<T: ?Sized + Sync, U: ?Sized + Sync> Sync // + 修复代码
for MappedMutexGuard<'_, T, U> {}

// PoC: this safe Rust code allows race on reference counter
* MutexGuard::map(guard, |_| Box::leak(Box::new(Rc::new(true))));
}