P.UNS.SAS.04 避免因为 Panic Safety 而导致双重释放

【描述】

要注意 Panic Safety 的情况,避免双重释放(double free)的问题发生。

在使用 std::ptr 模块中接口需要注意,容易产生 UB 问题,要多多查看 API 文档。

【反例】


#![allow(unused)]
fn main() {
//case 1
macro_rules! from_event_option_array_into_event_list(
    ($e:ty, $len:expr) => (
        impl<'e> From<[Option<$e>; $len]> for EventList {
                fn from(events: [Option<$e>; $len]) -> EventList {
                    let mut el = EventList::with_capacity(events.len());
                    for idx in 0..events.len() {
                    // 这个 unsafe 用法在 `event.into()`调用 panic 的时候会导致双重释放
                        let event_opt = unsafe { ptr::read(events.get_unchecked(idx)) };
                        if let Some(event) = event_opt { el.push::<Event>(event.into()); }
                    }
                    // 此处 mem::forget 就是为了防止 `dobule free`。
                    // 因为 `ptr::read` 也会制造一次 drop。
                    // 所以上面如果发生了 panic,那就相当于注释了 `mem::forget`,导致 `dobule free`
                    mem::forget(events);
                    el
                }
        }
    )
);
}

【正例】


#![allow(unused)]
fn main() {
macro_rules! from_event_option_array_into_event_list(
    ($e:ty, $len:expr) => (
        impl<'e> From<[Option<$e>; $len]> for EventList {
            fn from(events: [Option<$e>; $len]) -> EventList {
                let mut el = ManuallyDrop::new(
                    EventList::with_capacity(events.len())
                );

                for idx in 0..events.len() {
                    let event_opt = unsafe {
                        ptr::read(events.get_unchecked(idx))
                    };

                    if let Some(event) = event_opt {
                        // Use `ManuallyDrop` to guard against
                        // potential panic within `into()`.
                        // 当 into 方法发生 panic 当时候,这里 ManuallyDrop 可以保护其不会`double free`
                        let event = ManuallyDrop::into_inner(
                            ManuallyDrop::new(event)
                            .into()
                        );
                        el.push(event);
                    }
                }
                mem::forget(events);
                ManuallyDrop::into_inner(el)
            }
        }
    )
);
}