拓冰建站拓冰建站
首页 / 资讯中心 / 正文

comprehensive-rust 安全证明推论:为什么纯 Safe Rust 实现的函数必然 Sound

comprehensive-rust 安全证明推论为什么纯 Safe Rust 实现的函数必然 Sound【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust导读本文基于 Google Android 团队 Rust 课程comprehensive-rustunsafe 深潜章节中的《Soundness Proof (Part 2)》corollary.md展开。核心主题是回答一个问题纯 Safe Rust 写出来的函数为什么天生就是 sound健全的文章中我们将先梳理 sound / unsound 的正式定义与证明责任归属再逐条拆解该推论的证明步骤并结合仓库中三类典型 sound 代码形态纯 Safe、封装 unsafe、带文档化安全前置条件的 unsafe 函数的源码示例帮助读者在编写与审查 Rust 代码时准确判断一段代码是否健全、unsafe的证明负担到底落在谁身上。一、从定义出发什么是 sound健全的函数在进入推论之前需要先明确课程给出的正式定义。位于 soundness.md 中的定义如下sound 的函数只要它的安全前置条件safety preconditions被满足就不可能触发未定义行为UB的函数。这个定义有两个关键短语缺一不可安全前置条件被满足sound 不是无条件的。一个 sound 的 unsafe 函数允许声明前置条件例如传入的指针必须非空、必须指向合法内存只要调用方满足这些条件函数行为就保证良好不会出现 UB。不可能触发 UBsoundness 与内存安全问题深度绑定。rust-is-sound.md 明确指出soundness 是 Rust 的根基性原则可以通俗地理解为不可能引发内存安全问题。课程提醒一个容易被忽略的事实见 soundness.md 的授课要点编译器不会替你检查安全前置条件。负责满足前置条件的是实现调用方的那位程序员这是将 unsafe 函数设计得 sound 时必须牢记的责任边界。二、反面对照unsound不健全意味着什么unsoundness.md 给出了对照定义unsound 的函数即使你满足了文档中声明的所有安全前置条件它依然可能触发 UB。也就是说unsound 与调用方是否遵守规则无关——问题出在函数内部实现本身。unsoundness.md 中毫不含糊地写道Unsound code is BAD即便你完全按文档规则调用unsound 代码仍可能触发 UB。这一节还给出一条重要的工程准则仓库中不允许存在任何 unsound 代码而找出 unsound 代码是代码评审code review的首要目标。这与 corollary.md 的推论形成闭环因为纯 Safe Rust 的函数不存在 unsound 的可能所以审查者的注意力应当集中在包含unsafe的部分尤其是那些把证明负担转移给调用方的 unsafe 函数。三、核心推论纯 Safe Rust 实现的所有函数都是 sound 的corollary.md 在 sound 定义的基础上立刻导出一个推论Corollary所有用纯 Safe Rust 实现的函数都是 sound 的。其证明Proof只有三步逻辑非常简洁Safe Rust 代码没有安全前置条件。Safe Rust 由编译器保证内存安全不需要调用方额外承诺任何前提没有裸指针、没有可变静态、没有 union 访问等需要人工把关的约束。因此调用纯 Safe Rust 函数的调用方总是平凡地trivially满足这个空的前置条件集合。空集的条件当然永远成立调用方无需做任何额外论证。Safe Rust 代码不可能触发 UB。这是 Safe Rust 语言层面的根本保证。由 1–3 得证QED。把证明翻译成非正式语言课程给出的授课提示所有 Safe Rust 代码都是好代码——程序员不需要为它操心任何安全前置条件它总是按规则行事永远不会触发 UB。值得强调的是平凡满足空前置条件这一句这正是 sound 定义与 Safe Rust 交汇的地方。sound 要求前置条件满足 ⇒ 无 UB而 Safe Rust 的前置条件集合为空于是无 UB对任何输入都成立sound 自动成立。四、推论的另一面证明责任burden of proof落在谁身上推论的成立本质是把证明义务从程序员身上转移走了。课程在 3-shapes-of-sound-rust.md 中总结了 sound 代码只可能有三种形态以及各自的证明责任归属代码形态说明证明责任谁负责保证 sound纯 Safe 函数不含 unsafe 块没有任何 unsafe 块编译器Rust 编译器保证含 unsafe 块但完全封装的 safe 函数unsafe 块被封装在 safe 函数内部调用方无需知晓函数作者实现者含 unsafe 块且未封装的 unsafe 函数不封装把证明负担转移给调用方函数调用方须满足文档化的安全前置条件可见corollary.md 的推论对应表格第一行纯 Safe 函数把证明责任完全交给了编译器程序员不需要做任何证明工作。而一旦引入unsafe证明责任就会转移到作者或调用方身上——这正是后续章节copying-memory 系列要逐一剖析的内容。五、三种形态的代码佐证从纯 Safe 到文档化前置条件仓库中 copying-memory 目录用同一个copy拷贝字节切片函数演示了三种演进版本恰好对应上表三种形态也直观印证了推论。5.1 纯 Safe Rust 版本推论直接适用safe.md 展示了第一种形态pub fn copy(dest: mut [u8], source: [u8]) { for (dest, src) in dest.iter_mut().zip(source) { *dest *src; } }该实现只用 Safe Rust 的迭代器完成拷贝。课程指出无论传入什么参数这个copy都不可能触发内存安全问题——因为 Rust 的类型系统与借用检查器已经替你排除了以下所有隐患不存在别名aliasing问题借用检查器保证mut互斥悬垂指针不可能出现对齐必然正确不会意外读取未初始化的内存不需要手动处理空指针或越界检查。用推论的表述来说这个函数没有安全前置条件所以它是 sound 的。课程同时补充了一个澄清sound 不等于一定符合调用方的期望——如果dest空间不足copy只会拷贝一部分数据这是 zip 截断到较短长度的行为但这属于逻辑语义问题而非 UBsound 只承诺无 UB。5.2 封装 unsafe 的 Safe 函数作者承担证明责任encapsulated-unsafe.md 演示了第二种形态函数签名仍是 safe 的但内部通过get_unchecked/get_unchecked_mut手动访问内存绕开了迭代器pub fn copy(dest: mut [u8], source: [u8]) { let len dest.len().min(source.len()); let mut i 0; while i len { // SAFETY: i must be in-bounds as it was produced by source.len() let new unsafe { source.get_unchecked(i) }; // SAFETY: i must be in-bounds as it was produced by dest.len() let old unsafe { dest.get_unchecked_mut(i) }; *old *new; i 1; } }课程要点从调用方视角看这个函数依然是 safe 的签名没有 unsafe调用方无需了解内部细节从 soundness 角度看只要不可能存在任何输入触发内存安全问题含 unsafe 块的 Safe 函数就是 sound 的这里的证明责任在函数作者作者必须用内联的// SAFETY:注释说明为什么每个 unsafe 块在当前上下文是安全的本例如i由len约束必然在界内并保证循环逻辑不会引入越界访问。5.3 文档化安全前置条件的 unsafe 函数调用方承担证明责任documented-safety-preconditions.md 演示第三种形态函数被声明为unsafe fn并用# Safety文档段声明前置条件/// # Safety /// /// This function can easily trigger undefined behavior. Ensure that: /// /// - source pointer is non-null and non-dangling /// - source data ends with a null byte within its memory allocation /// - source data is not freed (its lifetime invariants are preserved) /// - source data contains fewer than isize::MAX bytes pub unsafe fn copy(dest: mut [u8], source: *const u8) { // ...内部通过 unsafe 解引用与 from_raw_parts 构造切片... }这一形态的关键约束对应 corollary 推论的反面soundness 不再自动成立。unsafe 函数要 sound必须同时满足两个条件安全前置条件被文档化# Safety段写得足够清楚函数内部每个 unsafe 块都配有// SAFETY:注释说明在哪些前置条件下该操作合法。课程还指出 main 中调用方的两种常见错误用来强调调用方承担证明责任a[114, 117, 115, 116]并不满足copy的数据以 null 字节结尾这一前置条件——直接调用会产生 UB在unsafe块中调用copy时需要写// SAFETY:注释逐一说明本次调用满足了哪些前置条件。这正是3 Shapes表格第三行的工程含义一旦进入 unsafe 函数编译器不再兜底证明责任移交调用方审查者应据此核查调用点的 SAFETY 注释是否完整、准确。六、总结如何运用这个推论回顾整条逻辑链soundness-proof.md 统领 soundness.md → unsoundness.md → corollary.md 三个小节sound 的定义前置条件满足时绝不触发 UBsoundness.mdunsound 的定义即便满足文档化前置条件仍可能触发 UBunsoundness.md推论本文核心纯 Safe Rust 函数没有前置条件、不可能触发 UB因此必然 soundcorollary.md。落到日常实践这个推论给开发者三条可操作准则能用纯 Safe Rust 实现就用纯 Safe Rust 实现——它把证明责任交给编译器是成本最低、最可靠的 sound 代码形态当需要unsafe时优先封装而非暴露——把 unsafe 块封装在 safe 函数内部并配齐// SAFETY:注释由作者承担证明责任调用方保持安全体验必须暴露 unsafe 函数时前置条件文档要精确、调用点注释要完整——unsafe 函数的 soundness 依赖于# Safety文档与调用点// SAFETY:注释的配合这也是代码评审中查找 unsound 代码的着力点。本文涉及的课程小节均位于 src/unsafe-deep-dive/rules-of-the-game 目录下其中 3-shapes-of-sound-rust.md 提供三种 sound 形态总览copying-memory 目录提供可运行的完整代码示例建议对照阅读以获得完整图景。【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门