加载失败
这篇讨论围绕一篇列出 uutils(一个用 Rust 重写 GNU coreutils 的项目)问题清单的文章展开。文章和评论聚焦于文件系统、权限和路径相关的漏洞,例如 TOCTOU、`chroot`、NSS、`kill -1`、非 UTF-8 路径处理,以及 `std::fs` 选择路径而不是句柄的接口设计。uutils 最早在 2013 年只是学习 Rust 的练习,后来却被 Canonical/Ubuntu 推向替换 coreutils 的方向,因此很多评论把它当成“学习项目升级为发行版基础设施”后的风险案例。讨论还牵涉 GPL 与 MIT 许可、GNU 测试套件、`openat`/`openat2` 之类更安全的文件操作接口,以及在 NFS 等远程文件系统上的性能退化。
很多评论强调 Rust 的强项主要是 memory safety,而不是自动消灭逻辑错误、语义误用或错误的 API 选择。`unwrap`、`expect`、panic、`kill -1` 这类问题都属于程序员写错了流程,编译器并不会替你修正。也有人指出,把几处失败当成 Rust 整体失灵,其实是在把“不会有所有 bug”这个从来不存在的承诺塞给 Rust。
[来源1] [来源2] [来源3] [来源4] [来源5] [来源6] [来源7] [来源8]
不少评论把锅更多归到 Rust 的 `std::fs`:它太偏向路径操作,几乎直接映射 Unix 传统 syscall,而不是提供更安全的抽象。有人希望标准库默认采用 `openat` / `openat2` 这类基于句柄的方式,减少 TOCTOU 和路径重解析带来的风险。也有人提到 `rustix`、`nix`、`openat` 等 crate 已经有更合适的封装,只是 `libstd` 还停留在最低公共分母。
[来源1] [来源2] [来源3] [来源4] [来源5] [来源6] [来源7] [来源8] [来源9] [来源10] [来源11]
另一条主线认为这些问题本质上是 Unix/POSIX 的历史包袱,不是换语言就能抹掉的。`chroot` 会牵出 NSS 动态加载,`kill` 的负值语义会变成广播信号,`PATH_MAX`、硬链接、符号链接和路径重解析也都埋着长期存在的陷阱。评论里多次提到,很多规则不是看函数名就能懂的,而是靠几十年事故和补丁慢慢学出来的。
[来源1] [来源2] [来源3] [来源4] [来源5] [来源6] [来源7] [来源8] [来源9] [来源10]
很多人直接批评开发者缺乏 Unix 实战经验,却去重写 coreutils 这种基础组件。有人举出把根目录判断写成 `file == Path::new("/")`、对严重错误处理得不对、以及 `expect`/`unwrap` 用得太随意,认为这更像系统编程入门期的失误。也有人补充说 uutils 最早只是 2013 年的 Rust 学习项目,后来才被推向替代方案,所以把学习代码升级成发行版基础设施,本身就很冒险。
[来源1] [来源2] [来源3] [来源4] [来源5] [来源6] [来源7] [来源8] [来源9] [来源10]
很多评论把焦点放在测试、兼容性和性能边界上,而不是单纯讨论语言安全。有人指出 uutils 确实在跑 GNU coreutils 的测试套件,但那更偏兼容性,未必能覆盖安全问题;真正的行为知识往往来自生产环境里逐步补出来的修复。性能上也有很具体的例子:在 NFS 上给目录里的每个文件都做 `stat`,会把一次简单目录遍历放大成成千上万次网络往返。
[来源1] [来源2] [来源3] [来源4] [来源5] [来源6] [来源7] [来源8] [来源9] [来源10] [来源11]
这场讨论还强烈指向 Canonical 和 Ubuntu 的发布决策。很多人认为,把仍在大量修补问题的 Rust 版 coreutils 推进到 Ubuntu 26.04 LTS 太激进,尤其在离发布不远时还暴出几十个 CVE。批评者把这看成营销、管理和招聘文化的问题,而不是 Rust 本身的问题;也有人干脆说要换发行版,或者直接保留 GNU 版本。
[来源1] [来源2] [来源3] [来源4] [来源5] [来源6] [来源7] [来源8] [来源9] [来源10] [来源11] [来源12] [来源13]
还有一部分争论围绕许可证和 clean-room 实现展开。uutils 明确表示不接受基于 GNU 源码的贡献,而且项目选择 MIT 许可,这让一些人认为他们为了避开 GPL 影响而主动放弃了直接借鉴成熟实现的机会。也有人反驳说 GPL 并不禁止阅读源码后自己实现,但无论法律解释如何,这种隔离策略确实会让项目更难继承前代积累下的修复和行为知识。
[来源1] [来源2] [来源3] [来源4] [来源5] [来源6] [来源7] [来源8] [来源9] [来源10]
TOCTOU: Time-of-check to time-of-use,先检查再使用之间的竞态,攻击者可趁机替换路径或链接。
openat / openat2: 基于 dirfd 的文件打开 syscall,更适合做相对目录、减少路径竞态和穿越攻击。
std::fs: Rust 标准库的文件系统模块,很多 API 以 Path 为中心,安全性并不自动高于原始 syscall。
NSS: Name Service Switch,Unix/Linux 用于用户名、主机名等解析的机制,常通过动态库加载后端。
chroot: 改变进程根目录的 Unix 机制/工具,若根目录可写或配合不当很容易出安全问题。
POSIX: Portable Operating System Interface,Unix-like 系统的行为标准,也是一堆历史兼容约束的来源。
unsafe block: Rust 中需要程序员手动保证安全不变量的代码块,编译器不再替你完全兜底。