在 Rust 中,unsafe
是危险但有时必需的应急路径。在编写或查看 unsafe
代码时,请务必:
- 明确确定每个
unsafe
块所需的所有假设和不变性; - 确保满足这些假设
- 确保继续满足这些假设。
为了确保 unsafe
的不变量不会被未来的编辑器破坏,每次使用 unsafe
时都必须附带清晰简洁的注释,解释所做的假设。
尽可能将不安全因素打包到单个函数或模块中,以便对外界提供安全的抽象概念。FFI 调用通常应通过安全函数公开,该函数的唯一用途就是为相关函数提供一个安全封装容器。这些函数应包含包含以下信息的注释(如果适用):
- 前提条件(例如,参数的有效状态有哪些?)
- 失败处理(例如,应该释放哪些值?已忘记?已失效?)
- 成功处理(例如,创建或使用了哪些值?)
示例:
impl Channel {
/// Write a message to a channel. Wraps the
/// [zx_channel_write](//docs/zircon/syscalls/channel_write.md)
/// syscall.
pub fn write(&self, bytes: &[u8], handles: &mut Vec<Handle>)
-> Result<(), Status>
{
let opts = 0;
let n_bytes = try!(usize_into_u32(bytes.len()).map_err(|_| Status::OUT_OF_RANGE));
let n_handles = try!(usize_into_u32(handles.len()).map_err(|_| Status::OUT_OF_RANGE));
// Requires that `self` contains a currently valid handle or ZX_HANDLE_INVALID.
// On success, all of the handles in the handles array have been moved.
// They must be forgotten and not dropped.
// On error, all handles are still owned by the current process and can be dropped.
unsafe {
let status = sys::zx_channel_write(self.raw_handle(), opts, bytes.as_ptr(), n_bytes,
handles.as_ptr() as *const sys::zx_handle_t, n_handles);
ok(status)?;
// Handles were successfully transferred, forget them on sender side
handles.set_len(0);
Ok(())
}
}
}
如果 unsafe
代码依赖于其他安全代码来确保正确性,则必须在相应的安全代码旁边添加注释,说明它必须支持的不变体和原因。依赖于多个函数行为的不变量会引起额外的审查,跨模块或跨 crate 不安全需要更加关注。依赖于第三方 crate 的正确行为的 unsafe
代码可能会被拒绝,而依赖于第三方类型的内部表示详情的 unsafe
代码将永远不会被接受。
最后,包含 unsafe
类型(如 *const
、*mut
或 UnsafeCell
)的 struct
定义必须包含一条注释,解释该类型的内部表示法不变体。如果使用 unsafe
类型执行突变,或者使用另一种类型的内存实现别名,则应说明它如何支持 Rust 的“别名 XOR 变更”要求。
如果出于安全原因故意省略了任何 derive
可特征,则必须添加注释,以防止未来的编辑者添加不安全的 impl。
上述规则适用于 unsafe
代码的任何添加或对现有 unsafe
代码的任何修改。
如需详细了解如何封装 unsafe
不变量,请参阅 Ralf Jung 的“不安全范围”和 Niko Matsakis 的“Tootsie Pop”模型。
不安全的评价
如需请求对 Rust 代码进行不安全的审核,请执行以下操作:
- 在
Rust>Unsafe
组件下提交 bug。在 bug 说明中添加指向您的评价的链接。 - 将“Fuchsia Rust Unsafe Reviews fuchsia-rust-unsafe-reviews@google.com”添加为 CL 的审核者。系统会随机选择审核人员,并将其分配给您的 CL。
如果您的审核具有时效性,请提高 bug 的优先级,并发表评论来说明您的情况。