All posts
Trending Tech

44 CVEs in Rust Coreutils: Why the Borrow Checker Isn't Enough

Huma Shazia29 April 2026 at 3:58 pm6 min read
44 CVEs in Rust Coreutils: Why the Borrow Checker Isn't Enough

Key Takeaways

44 CVEs in Rust Coreutils: Why the Borrow Checker Isn't Enough
Source: Hacker News: Best
  • 44 CVEs were found in uutils, the Rust reimplementation of GNU coreutils shipping in Ubuntu
  • Time-of-check to time-of-use (TOCTOU) race conditions formed the largest cluster of bugs
  • Rust's standard library APIs make path resolution vulnerabilities easy to introduce

In April 2026, Canonical disclosed 44 CVEs in uutils, the Rust reimplementation of GNU coreutils that ships by default in Ubuntu 25.10. The vulnerabilities came from an external audit commissioned ahead of the 26.04 LTS release. Every single one of them landed in production code written by experienced Rust developers. None were caught by the borrow checker, clippy lints, or cargo audit.

44 CVEs
Security vulnerabilities found in uutils, a Rust reimplementation of GNU coreutils, that Rust's safety features failed to catch

Matthias Endler, who analyzed the audit results, writes that this is the most concentrated look at where Rust's safety ends that you'll find anywhere right now. The uutils team shared their audit results in detail, giving the community a rare chance to learn from real production bugs.

The Core Problem: Path Resolution Between Syscalls

The largest cluster of bugs in the audit follows one pattern. You make a syscall to check something about a file path. Then you make another syscall to act on that path. Between those two calls, an attacker with write access to a parent directory can swap the path component for a symbolic link. The kernel resolves the path from scratch on the second call, and the privileged action lands on the attacker's chosen target.

This is called a time-of-check to time-of-use (TOCTOU) race condition. It's a classic security bug that has nothing to do with memory safety. Rust's borrow checker prevents use-after-free, double-free, and data races. It does not prevent logic errors in how you sequence system calls.

These bugs are serious enough that cp, mv, and rm are still GNU implementations in Ubuntu 26.04 LTS. The Rust versions weren't safe for privileged operations.

How Rust's Standard Library Makes This Easy to Get Wrong

Rust's standard library provides ergonomic APIs like fs::metadata, File::create, fs::remove_file, and fs::set_permissions. These are the functions you reach for first. They all take a path and resolve it every time, rather than taking a file descriptor and operating relative to that.

For a normal program, this is fine. For a privileged tool that needs to be secure against local attackers, it's a trap. The safe-looking API does the unsafe thing.

Case Study: CVE-2026-35355

Here's a simplified version of the bug from the install command implementation:

rust
// 1. Clear the destination
fs::remove_file(to)?;

// ...

// 2. Create the destination. The path is re-resolved here!
let mut dest = File::create(to)?; // follows symlinks, truncates
copy(from, &mut dest)?;

Between step 1 and step 2, anyone with write access to the parent directory can plant a symlink at the destination path. Point it to /etc/shadow. File::create follows the symlink, and the privileged process overwrites /etc/shadow with whatever the source file contained.

The fix uses OpenOptions::create_new(true):

rust
fs::remove_file(to)?;
let mut dest = OpenOptions::new()
    .write(true)
    .create_new(true)
    .open(to)?;
copy(from, &mut dest)?;

The documentation for create_new says: "No file is allowed to exist at the target location, also no (dangling) symlink." If the call succeeds, the file was created atomically. There's no window for an attacker to slip in a symlink.

What Rust Safety Actually Means

Rust guarantees memory safety in safe code. No use-after-free. No buffer overflows. No data races. These are significant wins. Memory corruption bugs are responsible for roughly 70% of critical vulnerabilities in C and C++ codebases.

But memory safety is not the same as "correct" or "secure." Rust doesn't prevent:

  • Race conditions in business logic
  • TOCTOU vulnerabilities in filesystem operations
  • Incorrect permission checks
  • Logic errors in cryptographic implementations
  • SQL injection (in unsafe database code)
  • Denial of service through algorithmic complexity

The uutils audit is a reminder that "rewrite it in Rust" fixes one class of bugs. It doesn't fix all bugs.

Lessons for Systems Programmers

If you're writing privileged tools in Rust, Endler's analysis suggests several practices:

  1. Prefer file descriptor operations over path operations when security matters
  2. Use OpenOptions::create_new(true) instead of File::create when creating files atomically
  3. Audit any code that makes multiple syscalls on the same path
  4. Don't assume the borrow checker catches all security bugs

Jon Seager, VP Engineering for Ubuntu, discussed Canonical's Rust adoption on the 'Rust in Production' podcast. Listeners appreciated his honesty about where Rust helps and where it doesn't.

Also Read
GitHub RCE Flaw: 88% of Enterprise Servers Still Vulnerable

Another critical infrastructure vulnerability disclosure affecting enterprise security

The Bigger Picture

Rust's adoption in systems programming continues to grow. The Linux kernel includes Rust. Android uses it. Microsoft and Google have both invested heavily. This is good. Memory safety bugs are a real and serious problem.

But the uutils audit shows that security requires more than memory safety. It requires correct logic. It requires understanding the threat model. It requires knowing which APIs are safe for privileged operations and which aren't.

The uutils team deserves credit for publishing the audit results. Most organizations would quietly fix the bugs and say nothing. Sharing the details lets everyone learn.

ℹ️

Logicity's Take

Frequently Asked Questions

What are TOCTOU vulnerabilities?

Time-of-check to time-of-use vulnerabilities occur when a program checks a condition and then acts on it, but an attacker changes the state between the check and the action. In filesystem operations, this often involves swapping a file for a symlink.

Does Rust prevent all security vulnerabilities?

No. Rust prevents memory safety bugs like buffer overflows and use-after-free. It does not prevent logic errors, race conditions, incorrect permission checks, or vulnerabilities in how syscalls are sequenced.

Why are cp, mv, and rm still GNU in Ubuntu 26.04?

The TOCTOU vulnerabilities in the Rust implementations made them unsafe for privileged operations. Until those bugs are fixed and verified, Ubuntu is shipping the GNU versions for these commands.

How do you prevent TOCTOU bugs in Rust filesystem code?

Use file descriptor operations instead of path operations where possible. Use OpenOptions::create_new(true) for atomic file creation. Avoid sequences where you check a path's properties and then act on the same path separately.

ℹ️

Need Help Implementing This?

Source: Hacker News: Best / Corrode Rust Consulting

H

Huma Shazia

Senior AI & Tech Writer

Related Articles

Tesla's Remote Parking Feature: The Investigation That Didn't Quite Park Itself
Trending Tech·8 min

Tesla's Remote Parking Feature: The Investigation That Didn't Quite Park Itself

The US auto safety regulators have closed their investigation into Tesla's remote parking feature, but what does this mean for the future of autonomous driving? We dive into the details of the investigation and what it reveals about the technology. The National Highway Traffic Safety Administration found that crashes were rare and minor, but the investigation's closure doesn't necessarily mean the feature is completely safe.