P.UNS.SAS.02 Unsafe 代码编写者有义务检查代码是否满足安全不变式
【描述】
安全不变式(见 Unsafe 代码术语指南 )是 Rust 里的安全函数,在任何有效输入的情况下,都不应该发生任何未定义行为。
可以从以下三个方面来检查:
- 逻辑一致性。
- 纯洁性。相同的输入总是要返回相同的输出。
- 语义约束。传入的参数要合法,满足数据类型。
【正例】
该代码是为 Borrow<str>
实现 join 方法内部调用的一个函数 join_generic_copy
的展示。 在 join_generic_copy
内部,会对 slice
进行两次转换,而在 spezialize_for_lengths!
宏内部,调用了 .borrow()
方法,如果第二次转换和第一次不一样,而会返回一个未初始化字节的字符串。
这里, Borrow<B>
是高阶类型,它内部 borrow
的一致性其实并没有保证,可能会返回不同的 slice,如果不做处理,很可能会暴露出未初始化的字节给调用者。
#![allow(unused)] fn main() { // CVE-2020-36323: a higher-order invariant bug in join() fn join_generic_copy<B, T, S>(slice: &[S], sep: &[T]) -> Vec<T> where T: Copy, B: AsRef<[T]> + ?Sized, S: Borrow<B> { let mut iter = slice.iter(); // `slice`is converted for the first time // during the buffer size calculation. let len = ...; // `slice` 在这里第一次被转换 let mut result = Vec::with_capacity(len); // ... unsafe { let pos = result.len(); let target = result.get_unchecked_mut(pos..len); // `slice`is converted for the second time in macro // while copying the rest of the components. spezialize_for_lengths!(sep, target, iter; // `slice` 第二次被转换 0, 1, 2, 3, 4); // Indicate that the vector is initialized result.set_len(len); } result } // PoC: a benign join() can trigger a memory safety issue impl Borrow<str> for InconsistentBorrow { fn borrow(&self) -> &str { if self.is_first_time() { "123456" } else { "0" } } } let arr: [InconsistentBorrow; 3] = Default::default(); arr.join("-"); }