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) } } ) ); }