Rust Coreutils 0.9.0 Release:
We are excited to announce the release of Rust Coreutils 0.9.0 - a release focused on safety and security. This cycle was shaped by a third-party security audit, driving extensive TOCTOU hardening and a sustained, project-wide effort to shrink the amount of unsafe code by removing it outright and migrating low-level syscalls from nix/libc to rustix. On top of that, we landed major zero-copy I/O performance work (splice/tee/pipe), broadened WebAssembly, Cygwin and Windows support, and continued contributing tests and bug reports upstream to GNU coreutils.
GNU Test Suite Compatibility:
──────┬─────┬─────┬─────────────────────┬─────────────┬─────────────┬───────────────────────
Result│0.8.0│0.9.0│Change 0.8.0 to 0.9.0│% Total 0.8.0│% Total 0.9.0│% Change 0.8.0 to 0.9.0
──────┼─────┼─────┼─────────────────────┼─────────────┼─────────────┼───────────────────────
Pass │630 │625 │-5 │94.74% │90.58% │-4.16%
──────┼─────┼─────┼─────────────────────┼─────────────┼─────────────┼───────────────────────
Skip │14 │8 │-6 │2.11% │1.16% │-0.95%
──────┼─────┼─────┼─────────────────────┼─────────────┼─────────────┼───────────────────────
Fail │21 │56 │+35 │3.16% │8.12% │+4.96%
──────┼─────┼─────┼─────────────────────┼─────────────┼─────────────┼───────────────────────
Error │0 │1 │+1 │0% │0.14% │+0.14%
──────┼─────┼─────┼─────────────────────┼─────────────┼─────────────┼───────────────────────
Total │665 │690 │+25 (new tests) │ │ │
──────┴─────┴─────┴─────────────────────┴─────────────┴─────────────┴───────────────────────
Note: The rise in failing tests is due to the upstream GNU test suite being extended, not to regressions on our side. We updated our GNU reference from 9.10 to 9.11 (PR #11922), which added 25 new tests (665 → 690). Many of these newly-introduced tests are not yet passing, which accounts for the jump from 21 to 56 failures; no previously-passing functionality regressed, and work is ongoing to address the new tests.
Highlights:
- Security Hardening (Zellic audit)
- A third-party security audit by Zellic reviewed the codebase; the findings - widely reported as 44 CVEs (see also the Ubuntu update and the corrode write-up Bugs Rust won't catch) - were concentrated in TOCTOU races and filesystem edge cases that Rust's type system does not prevent
- It's worth noting that many of these CVEs are not memory-safety issues but differences in behavior from GNU coreutils that the audit identified; aligning our semantics with GNU resolves them
- This release closes many of them: a new TOCTOU-resistant uucore::safe_copy module; TOCTOU fixes in cp, mv, and chmod recursive traversal; rm dot/dotdot path-parsing protection; nohup.out now created with mode 0600; and chroot now resolves all ids before chrooting
- Reducing unsafe & migrating to rustix
- A sustained, project-wide push to shrink the unsafe surface: dozens of unsafe removals across utilities, tests, fuzz targets and uucore (get_groups, make_fifo, build.rs, and more)
- Migration from nix/libc to rustix across id, tr, timeout, sort, wc, tail, cp, who, factor, and core process/IO paths
- Performance: zero-copy I/O
- splice()/tee()/pipe() fast paths landed across cat, wc, head, tail, yes, cp, tee, and unexpand (e.g. unexpand +7.5%, faster cp from a pipe on Linux, tee via raw syscalls, yes using the tee syscall)
- Consolidated in a reworked uucore::pipes / buf_copy
- GNU Compatibility & Upstream Collaboration
- Reference bumped to GNU 9.11, which extended the suite with 25 new tests (the reason the failure count went up - see the note above; it is not a regression)
- We continue to contribute tests and bug reports upstream to GNU coreutils, and our compatibility work keeps surfacing edge cases on both sides
- New fixes across numfmt, date, tr, cksum, factor, head, stat, and sort
- numfmt Overhaul
- Precision handling, rejection of scientific notation, LC_NUMERIC decimal separator, zero-padding for negative numbers, IEC precision cap, large %f values, --to=auto exit code, and multi-byte --suffix width accounting
- ls Improvements
- ls -lF symlink target indicators, link-count column no longer inflated per-ACL-file, version-sorting and recursive-mode fixes, independent permissions-column width
- Refactor letting crate users (e.g. nushell) call ls without writing everything to stdout
- Cross-platform Reach
- WASI/wasm support for ln, dd, mktemp, and tty.wasm; Cygwin CI and builds (date, stdbuf); Windows tty (incl. msys2 paths); OpenBSD triage improvements
- Internationalization
- numfmt and du now honor LC_NUMERIC for the decimal separator
- Contributions: This release was made possible by 30 new contributors joining our community
Call to Action:
Try it in your browser - Online Playground powered by WebAssembly
Help us translate - Contribute to Rust Coreutils on Weblate
Sponsor us on GitHub to accelerate development: github.com/sponsors/uutils
What's Changed
cat
- cat: avoid unnecessary allocation by @oech3 in #11675
- cat: do not splice() on FUSE by @oech3 in #11723
- cat: replace copy_exact() with write_all by @oech3 in #11800
- cat: remove collapsible_if by @oech3 in #11884
- cat: call pipe() once with multiple input by @oech3 in #11699
- cat: avoid unnecessary alloc on Linux by @oech3 in #11906
- cat: remove unsafe from test code by @oech3 in #12027
- cat: simplify match by @oech3 in #12178
- cat: remove line buffering by @oech3 in #12190
- cat: dedup print_unbuffered by @oech3 in #12214
- cat: remove splice.rs by @oech3 in #12404
chroot
- chroot: start removing unsafe by @oech3 in #12053
cksum
- cksum: fix parsing error with tagged cheksum files by @frenchua in #11704
- cksum: get rid of print! and println! to avoid panicking on write errors by @RenjiSann in #12099
- cksum: Fix --check filename escaping for 9.11 by @RenjiSann in #12156
- cksum: Introduce HashLength newtype to avoid byte/bit misuse by @RenjiSann in #12235
coreutils
- coreutils: --help args is same with --help by @oech3 in #11367
- coreutils: fix panic on linux < 6.4 when /proc is not mounted by @oech3 in #12104
cp
- cp: improve throughput with pipe input (Linux only) by @oech3 in #11798
- cp: fix fail-perm GNU test adaptation by @karanabe in #12119
- chore(cp): : move raw libc calls to rustix by @Alonely0 in #12112
date
- date: replace regex with hand-written format spec parser by @sylvestre in #11758
- date: use cfg unix by @oech3 in #11952
- date: width prefix in %N format specifier is ignored ( %3N, %6N always output full 9 nanosecond digits) #12001 by @Devel08 in #12010
- date: fix build for cygwin by @oech3 in #12309
- date: re-zone trailing TZ abbreviation input to local timezone by @0xSoftBoi in #12327
dd
- dd: remove collapsible_if by @oech3 in #11885
- dd: exit when bs/ibs/obs/cbs isn't positive by @iburaky2 in #11630
- dd: support wasm32-wasip1 by @pocopepe in #12258
- dd: fix OOM panic with skip=1 by @oech3 in #12399
df
- df: get rid of unnecessary clone calls by @fogti in #12073
dirname
- dirname: get rid of needless Cow by @ChrisDenton in #11960
du
- du: fix error from needless_borrow lint on FreeBSD by @cakebaker in #11791
- du: remove unnecessary implementation of code() by @cakebaker in #11803
- du: make du -a -s error by @oech3 in #11917
- du: Allow command-line argument that appears more than once by @ajanicij in #11897
- refactor(du): simplify error handling by @xtqqczze in #11957
- du: propagate errors from --exclude-from instead of panicking by @rossilor95 in #11996
- du: honor LC_NUMERIC for decimal separator by @eyupcanakman in #12357
echo
- echo: add benchmark by @oech3 in #12091
env
- tests(env): increase signal delivery delay to fix flaky macOS CI by @sylvestre in #11829
expr
- expr: avoid panic on regex retry-limit by @eyupcanakman in #11833
factor
- factor: emit GNU 9.11 'is not a valid positive integer' for stdin input by @sylvestre in #12137
- factor: remove unsafe str::from_utf8_unchecked by @oech3 in #12142
fmt
- fmt: remove unwrap() by @oech3 in #11810
head
- head: remove collapsible_if by @oech3 in #11883
- refactor(head): rename read functions to print for clarity by @xtqqczze in #11988
- head: remove print(ln) macros by @oech3 in #11974
- head: catch I/O error with -c 1 by @oech3 in #12030
- head: strip os error suffix from stdout write error message by @sylvestre in #12121
- head: fix -c0 and -n0 on directories to match GNU behavior by @sylvestre in #12217
- head/tail: use OPTION rather than FLAG by @fnadeau in #12301
id
- fix(id): refine feature checks for SELinux and SMACK support by @xtqqczze in #12047
- id: replace nix by rustix by @oech3 in #12164
install
- fix(install): follow symlink components in destination path with -D by @abendrothj in #11505
- install: update comments and tests after symlink follow behavior change by @Ecordonnier in #12475
join
- join: remove collapsible_if by @oech3 in #11901
- join: use show_error instead of eprintln by @cakebaker in #11932
ln
- ln: Improve to LnError and make public fns for nushell by @dmatos2012 in #12009
- ln: add WASI support via symlink_path by @DePasqualeOrg in #11713
logname
- fix(logname): return error exit code when there is an error by @rodrigost23 in #9313
ls
- ls: drop #[allow(clippy::needless_bool)] by @oech3 in #11739
- ls: fix link-count column inflating per ACL file by @sylvestre in #11781
- refactor uu_ls so that crate users can call the ls without having to print everything to stdout by @fdncred in #9851
- ls: restore WASI ".." metadata fallback in collect_directory_entries by @sylvestre in #11930
- ls: use cfg unix by @oech3 in #11953
- ls: fix comparisons in version sorting and recursive mode by @Alonely0 in #12108
- ls: hide paths argument by @cakebaker in #12113