Hacker News 中文摘要

RSS订阅

Rust无法捕获的漏洞 -- Bugs Rust won't catch

文章摘要

文章指出,尽管Rust语言在内存安全方面有优势,但uutils项目(Rust实现的GNU coreutils)仍被发现44个安全漏洞。这些漏洞由专业开发者编写,却未被Rust的借用检查器、Clippy或cargo audit等工具捕获。作者强调此事并非批评开发团队,而是希望通过公开审计结果帮助社区学习改进。

文章总结

Rust无法捕获的漏洞类型分析

背景

2026年4月,Canonical披露了Rust版GNU核心工具uutils中的44个CVE漏洞,这些漏洞均未被Rust的借用检查器、clippy或cargo audit捕获。本文通过分析这些漏洞,总结了Rust在系统编程中的安全边界。

主要漏洞类型及案例

  1. 跨系统调用的路径信任问题

    • 模式:检查路径后执行操作时,攻击者可通过符号链接劫持路径。
    • 案例install工具在删除文件后创建新文件时,未验证路径是否被篡改(CVE-2026-35355)。
    • 修复方案:使用OpenOptions::create_new(true)确保路径未被占用。
  2. 权限设置时机不当

    • 问题:先创建目录后设置权限会导致短暂的安全窗口期。
    • 规则:应通过DirBuilderExt::mode()在创建时直接设置权限。
  3. 路径字符串比较的陷阱

    • 案例chmod--preserve-root检查仅比较字面路径/,忽略/../等变体。
    • 修复方案:使用fs::canonicalize解析路径后再比较。
  4. 字节与字符串的边界处理

    • 案例comm工具错误地将非UTF-8字节转换为替换字符(CVE-2026-35346)。
    • 规则:系统工具应优先使用&[u8]OsStr处理原始数据。
  5. panic导致的拒绝服务

    • 案例sort --files0-from因非UTF-8路径而崩溃(CVE-2026-35348)。
    • 规则:对不可信输入应返回错误而非panic,并启用严格lint检查。
  6. 错误传播缺失

    • 案例chmod -R仅返回最后操作的错误码,掩盖了中间失败。
    • 规则:应保留最严重的错误状态。
  7. 与原工具的行为差异

    • 案例kill -1被误解析为向所有进程发信号(CVE-2026-35369)。
    • 规则:需严格兼容历史行为,避免脚本依赖的隐式约定。
  8. 信任边界前的输入解析

    • 案例chroot后在攻击者控制的文件系统中解析用户(CVE-2026-35368)。
    • 规则:敏感操作前应完成所有解析。

Rust的积极成果

尽管存在上述问题,Rust版工具未出现以下经典C漏洞: - 缓冲区溢出 - 释放后使用 - 数据竞争 - 空指针解引用

核心建议

  • 系统编程原则:使用文件描述符而非路径,优先选择OsStr而非String
  • 防御性编程:通过CI运行原版测试套件,采用checked_*等安全操作。
  • 正确性即习惯:Rust的"地道"写法不仅关乎优雅,更需真实反映系统复杂性。

延伸阅读

(注:本文案例基于虚构的2026年CVE编号,实际分析适用于当前Rust系统编程实践。)

评论总结

评论总结:

  1. Rust开发者的Unix经验不足

    • 观点:Rust代码库中的错误源于开发者对Unix API和语义的理解不足,而非Rust本身的问题。
    • 引用:
      • "They knew how to write Rust, but clearly weren't sufficiently experienced with Unix APIs..." (评论1)
      • "这些错误在长期开发GNU coreutils的人看来非常业余。" (评论1)
  2. Rust并非万能,无法避免逻辑错误

    • 观点:Rust虽能防止内存安全问题,但无法避免因人为或AI导致的逻辑错误。
    • 引用:
      • "Rust不能防止其他类型的漏洞,比如逻辑错误。" (评论6)
      • "Rust promised you memory safety... but these 44 CVEs are the receipt." (评论12)
  3. 重写代码的挑战

    • 观点:重写代码时容易忽略原代码中隐含的历史教训,导致新问题。
    • 引用:
      • "原始代码是随着实际问题的出现逐步演变的,重写时很难完全复现这些经验。" (评论11)
      • "除非有详细的文档和测试用例,否则很难避免这些问题。" (评论11)
  4. Rust标准库的不足

    • 观点:Rust标准库在文件系统操作(如TOCTOU竞争条件)方面存在缺陷。
    • 引用:
      • "在Rust中,使用std::fs很容易写出TOCTOU竞争条件的代码。" (评论4)
      • "希望标准库能提供类似openat的API。" (评论4)
  5. 性能与安全性的权衡

    • 观点:某些安全措施(如路径解析)可能带来显著的性能开销。
    • 引用:
      • "解析路径的性能开销很大,GNU软件通常努力避免这种限制。" (评论4)
      • "示例中显示,Rust实现的cp比GNU cp慢很多。" (评论4)
  6. Rust的积极防御

    • 观点:Rust虽不完美,但相比其他语言仍能防止更多问题。
    • 引用:
      • "Rust不能防止所有错误,但其他语言也不能,而且Rust能防止更多内存安全问题。" (评论8)
      • "应该比较GNU coreutils早期和Rust重写版的漏洞数量。" (评论8)
  7. 文件系统操作的复杂性

    • 观点:文件系统操作(如符号链接和硬链接)的复杂性可能导致安全问题。
    • 引用:
      • "攻击者可以通过父目录的写权限操纵硬链接,几乎没有缓解措施。" (评论13)
      • "文件系统不关心你的借用检查器。" (评论12)
  8. 工具与文档的重要性

    • 观点:工具(如测试套件)和文档对避免错误至关重要。
    • 引用:
      • "uutils现在运行GNU coreutils的测试套件,这是最低限度的防御。" (评论7)
      • "希望有人能整理出所有可能加载共享库的函数列表。" (评论14)

总结:

评论主要围绕Rust在重写Unix工具时的局限性展开,包括开发者经验不足、逻辑错误无法避免、文件系统操作的复杂性等。尽管Rust在内存安全方面有优势,但其标准库和性能问题仍需改进。同时,重写历史代码的挑战和工具支持的重要性也被多次提及。