惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
阮一峰的网络日志
阮一峰的网络日志
Apple Machine Learning Research
Apple Machine Learning Research
爱范儿
爱范儿
WordPress大学
WordPress大学
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
J
Java Code Geeks
罗磊的独立博客
S
SegmentFault 最新的问题
V
V2EX
V
Visual Studio Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
美团技术团队
博客园 - 三生石上(FineUI控件)
Stack Overflow Blog
Stack Overflow Blog
Y
Y Combinator Blog
MyScale Blog
MyScale Blog
D
Docker
Google DeepMind News
Google DeepMind News
Blog — PlanetScale
Blog — PlanetScale
M
Microsoft Research Blog - Microsoft Research
Martin Fowler
Martin Fowler
S
Secure Thoughts
B
Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Recent Announcements
Recent Announcements
MongoDB | Blog
MongoDB | Blog
C
Cisco Blogs
C
CERT Recently Published Vulnerability Notes
T
True Tiger Recordings
GbyAI
GbyAI
P
Proofpoint News Feed
P
Privacy International News Feed
Jina AI
Jina AI
The Cloudflare Blog
I
Intezer
AWS News Blog
AWS News Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
S
Security Archives - TechRepublic
NISL@THU
NISL@THU
The Register - Security
The Register - Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
P
Palo Alto Networks Blog
S
Schneier on Security
L
LINUX DO - 热门话题
C
CXSECURITY Database RSS Feed - CXSecurity.com
Security Latest
Security Latest
C
Cybersecurity and Infrastructure Security Agency CISA

DEV Community

An open source LLM eval tool with two independent quality signals Using Dashboard Filtering to Get Customer Usage in Seconds from TBs of Data Skills, Java 17, And Theme Accents 4 Hard Lessons on Optimizing AI Coding Agents Arctype: Cross-Platform Database GUI for LLM Artifacts Your robots.txt says GPTBot is welcome. Your server says 403. Organizing How to Use AWS Glue Workflow 5 n8n Automations Every Digital Agency Should Be Running (Bill More, Work Less) Getting Started with TorchGeo — Remote Sensing with PyTorch Designing a Scalable Cross-Platform Appium Framework Google Antigravity 2.0 & Slash Commands Building a Unified Adaptive Learning Intelligence with Gemma 4, Flutter, and Multi-Model Orchestration Looking for beta testers for a £60 server management application The Disk-Pressure Incident That Taught Me to Always Set LimitRanges and Other Lessons from Mirroring EKS Locally. Why AI Should Not Write SQL Against ERP Databases Vibe coding works until it doesn't. The debt is real. Shipping at the Edge: Migrating a Coffee Subscription Platform to Cloudflare Workers Stop Tab-Switching: A Developer's Guide to Color Tools That Actually Fit the Workflow DevOps vs MLOps vs AIOps: What Changes, What Stays, and a Simple Roadmap to Get Started Run Powerful AI Coding Locally on a Normal Laptop 5 n8n Automations Every WooCommerce Store Needs (Save 10+ Hours/Week) What I Learned Building My Own AI Harness Hytale Servers Will Fail Treasure Hunts Until We Fix Our Event Handling Redux in React: Managing Global State Like a Pro Unfreezing Your GitHub Actions: Troubleshooting Stuck Deployments and Protecting Your Git Repo Statistics Unlocking Project Discoverability on GHES: A Key to Software Engineering Productivity When the Cleanup Code Becomes the Project Rockpack 8.0 - A React Scaffolder Built for the Age of AI-Assisted Development Mismanaging the Treasure Hunt Engine in Hytale Servers Will Get You Killed Why Hardcoded Automations Fail AI Agents Stop Calling It an AI Assistant. It’s Already Managing Your Company Why I built a post-quantum signing API (and why JWT is on borrowed time) Weekend Thought: Frontend Build Tools Suffer From Work Amnesia A 10-Line Playwright Trick That Saved Me Hours on Every Sephora Run AI Is Changing Engineering Culture More Than We Realize Everyone Was Focused on Gemini, But Infinite Scaler Was the Real Twister "Gemma 4 Analyzed My Bank Statements – Apparently I 'Have a Problem' with Coffee and Late-Night Apps" #css #webdev #beginners #codenewbie The Hidden Layer Every AI Developer Must Learn AlphaEvolve: Google DeepMind's Gemini-Powered Evolutionary Coding Agent RDS Reserved Instance Pricing: Every Engine, Every Rule, Real Dollar Savings How To Build An AI-Powered MVP Without Burning Your Startup Budget In 2026 Reading a Psychrometric Chart Without Getting Lost LMR-BENCH: Can LLM Agents Reproduce NLP Research Code? (EMNLP 2025) How to turn text into colors (without AI) Building Real-Time Apps in Node.js with Rivalis: WebSockets, Rooms, Actors, and a Binary Wire This Week In React #282 : Security, Fate, TanStack, Redux, Jotai | Hermes-node, Expo, Rozenite, Harness | TC39, Bun, pnpm, npm, Yarn, Node AI Copilot vs AI Agent Architecture - What's Actually Different (And Why It Matters) Smart Contract Security: NEAR's Futures Surge and AI Token Risks Database Maintenance: Tracing Production Incidents to Their Root Cause Stop juggling AI SDKs in PHP — meet Prisma Google Quietly Changed What “Apps” Mean at I/O 2026 The Infrastructure Team Is the Real Single Point of Failure Building SQLite from Scratch: 740 Lines of C++23 to Understand Every Byte of a .db File The 4 Levels of Hermes Agent Scaling Framework: From One Hermes Agent to a Fully Automated Team Your AI Has a Memory. It Just Doesn’t Know What to Remember. Claprec: Engineering Tradeoffs - Limited time vs. Perfection (6/6) Building a Daily Google News API Monitor in Python Building RookDuel Avikal: From Chess Steganography to Post-Quantum Archival Security Google I/O e IA: o que realmente muda na vida do dev? Color Contrast Failures: The Number One Accessibility Issue and How to Fix It # I Watched 15 Hours of Hermes Agent Videos So You Don't Have To Cómo solucionar el bucle infinito en useEffect con objetos y arrays en React The First Agent-Centric Cloud Security Platform — And Why We Didn't Build It That Way On Purpose Most Treasure Hunts Engines on Hytale Servers Are Built to Fail - Lessons from a Burned Database GhostScan v3.0 — From Closed-Source EXE to Open-Source Pentest Framework De hojas de cálculo a IA: construyendo una plataforma SRM moderna When is AI fine in education? Python Tools for Managing API Rate Limits in Data Pipelines How to Implement Exponential Backoff for Rate-Limited APIs in Python "My Web Chat Wasn't a Real Channel. That Broke My Agent Pipeline" next-advanced-sitemap v1.0.7 — safer URL ingestion & automatic trimming for Next.js sitemap generation I keep seeing people build an AI lead processing agent when they really need a 6-step rules engine AI Powered Student Learning Assistant Using Gemma 4 How I Built a Drop-In Proxy to Slash My OpenAI Bills by 20%+ Automatically Building a Sarcastic AI English Tutor with Persona-as-Code and Gemini Audio Input for Pronunciation Correction Five Years Later, I Finally Have 96GB VRAM — What It Actually Unlocks for Agent Loops Turning a 1-Line Idea Into a 40-Second Short with a 10-Beat Local Video Pipeline Running LTX-2.3 Alongside TTS on a Single 96GB GPU with a Cold-Start Architecture Cutting LTX-2 22B Peak VRAM by 40% with fp8_cast — and Why optimum-quanto Was a Trap HiDream Skeleton Mode: Prompt Beats OpenPose Ref — 8 Patterns Benchmarked Replicating a Language-Learning Comedy Short with Claude Code — Gemini as a Multimodal Sub-Agent HiDream-O1-Image 3–8x Faster: Benchmarking Steps, CFG, and Resolution AWS Savings Plan Buying Strategy: How to Layer, Size, and Time Commitments application.properties I built a macro tracker powered by AI + attitude Solace: A Global Mental Health First Responder Built with Gemma 4 Why Blocking Prompt Injection Is Wrong — and What to Do Instead The AI code tools Dutch developers actually use in 2026 (field notes) Automatic Error Recovery in AI Agent Networks You Are Not Choosing Building a Cinematic Adaptive Learning Intelligence with Gemma 4, Gemini, and OpenAI(Powered by Gemma 4) CLAUDE.md for Angular: 13 Rules That Make AI Write Idiomatic, Production-Ready Components I tested 7 vector databases for my RAG stack in 2026, here's the one nobody is talking about (yet) Claude agreed with a false fact I gave it. Confidently. That broke my workflow Google's "Budget" Model Just Beat Its Own Flagship. Here's What That Actually Means for Developers. How I built a monitoring SaaS for Joomla, WordPress & PrestaShop agencies Shifting from Passive Dashboards to Automated Remediation: A Guide to Next-Generation FinOps and CloudZero Alternatives Automating CSV WooCommerce Imports Without Plugins Why Wobbly Plugs and Overheating Outlets Are More Dangerous Than You Think (UL 498 Explained)
What 44 CVEs Tell You About Rust's Safety Boundary
Arthur · 2026-05-19 · via DEV Community

In April 2026, Canonical disclosed 44 CVEs in uutils, the Rust reimplementation of GNU coreutils that has been the default in Ubuntu since 25.10. The disclosures came out of an external audit commissioned ahead of the 26.04 LTS release. Most of the bugs were found by code review of a single Rust codebase. None of them were caught by the borrow checker, by clippy lints, or by cargo audit.

The audit is the sharpest case study available for what Rust catches and what it doesn't. The most useful read on the list is Matthias Endler's Bugs Rust Won't Catch, published April 29 on the corrode.dev blog. Endler runs corrode, a Rust consultancy; he hosts the Rust in Production podcast and recently had Canonical's VP of Engineering, Jon Seager, on it. His piece is the analytical companion to the Canonical disclosure: 44 CVEs, sorted into eight categories, with the actual git diff for the fix on most of them.

What follows takes Endler's enumeration as the spine and stacks two more arguments on top — the GNU-coreutils-maintainer testimony from the HN thread, which lands a benchmark that Endler's preferred fix doesn't survive, and the structural argument about what 40 years of accreted POSIX scars do to any rewrite, regardless of language.

What the audit caught, by category

Endler enumerates eight categories. The shape of each is the same: a Rust idiom that the type system endorses, applied in a context where the type system can't see what's wrong.

TOCTOU on path operations is the largest cluster, and the reason cp, mv, and rm are still the GNU implementations in 26.04 LTS rather than uutils. The pattern is one syscall to check, another to act, both taking a &Path. Between them, an attacker with write access to a parent directory can swap the path component for a symlink, the kernel re-resolves on the second call, and the privileged action lands on the attacker's chosen target. The clearest case is CVE-2026-35355 in install:

// 1. Clear the destination
fs::remove_file(to)?;
// ...
// 2. Re-resolves the path. Follows symlinks, truncates.
let mut dest = File::create(to)?;
copy(from, &mut dest)?;

Enter fullscreen mode Exit fullscreen mode

Anyone with write access to the parent directory can plant to as a symlink to /etc/shadow between steps 1 and 2; the privileged process happily overwrites it with whatever from contains. The fix uses OpenOptions::create_new(true), which the docs explicitly say "No file is allowed to exist at the target location, **also no (dangling) symlink."

Permission-set-after-create is the close TOCTOU relative. fs::create_dir(&path)?; fs::set_permissions(&path, ...)?; exposes a window where the directory exists with default permissions and any local user can open() it for a file descriptor that survives the later chmod. The fix is OpenOptions::mode() and DirBuilderExt::mode() so the file or directory is born with the permissions you wanted.

Path string equality is not filesystem identity. The original --preserve-root check in chmod was literally if recursive && preserve_root && file == Path::new("/") { return Err(PreserveRoot); }. That comparison is bypassed by anything that resolves to / but isn't spelled //../, /./, a symlink, anything canonicalize would resolve. Run chmod -R 000 /../ and watch it lock down the whole system. The category's most absurd member is CVE-2026-35363:

rm .    # ❌
rm ..   # ❌
rm ./   # ✅
rm ./// # ✅

Enter fullscreen mode Exit fullscreen mode

rm rejected the bare . and .. but happily accepted ./, deleted the current directory, then printed "Invalid input." The string comparison didn't survive a trailing slash.

UTF-8 versus raw bytes at Unix boundaries. Rust's String and &str are always UTF-8. Unix paths, environment variables, and the byte streams flowing through tools like cut, comm, and tr are not. Every place a Rust program bridges the two, it has three choices: lossy conversion (which silently rewrites invalid bytes to U+FFFD — fancy data corruption, in Endler's phrasing), strict conversion (which crashes on the first non-UTF-8 byte), or staying in OsStr / &[u8]. The audit found bugs in both of the first two. CVE-2026-35346 in comm used String::from_utf8_lossy, so passing a binary file through comm silently mangled the output. The fix replaced print! with BufWriter::write_all, staying in bytes.

Panic-as-DoS. Every unwrap, every expect, every slice index, every unchecked arithmetic operation in input-handling code is a potential denial of service if an attacker can shape the input. CVE-2026-35348 in sort --files0-from called expect() on a UTF-8 conversion of each filename:

$ python3 -c "open('list0','wb').write(b'weird\xffname\0')"
$ coreutils sort --files0-from=list0
thread 'main' panicked at uu_sort-0.2.2/src/sort.rs:1076:18:
Could not parse string from zero terminated input.

Enter fullscreen mode Exit fullscreen mode

GNU sort treats filenames as raw bytes — that's what filenames are. The uutils version aborted on the first non-UTF-8 path. As Endler puts it: "Your nightly cron job is dead and there goes your weekend."

Discarded errors. chmod -R and chown -R returned the exit code of the last file processed instead of the worst one. chmod -R 600 /etc/secrets/* could fail on half the files and exit 0. dd called Result::ok() on its set_len() to mimic GNU's /dev/null behavior, except the same code ran for regular files, so a full disk silently produced a half-written destination.

Behavioral incompatibility with GNU. Endler observes that "a surprising number of these CVEs aren't 'the code does something unsafe' but 'the code does something **different* from GNU, and a shell script somewhere relied on the GNU behavior.'"* The clearest case is CVE-2026-35369 in kill: GNU reads kill -1 <PID> as "signal 1, this PID." uutils read it as "send the default signal to PID -1," which on Linux means every process you can see. A typo turns into a system-wide kill switch.

Resolve before you cross. CVE-2026-35368 is the worst single bug in the audit. It's local root in chroot. Simplified pattern:

chroot(new_root)?;
// Still uid 0, but now inside the attacker-controlled filesystem.
let user = get_user_by_name(name)?;
setgid(user.gid())?;
setuid(user.uid())?;
exec(cmd)?;

Enter fullscreen mode Exit fullscreen mode

get_user_by_name goes through NSS, which dlopens libnss_* modules at runtime. After the chroot, those modules are loaded from the new root filesystem. An attacker who can plant a file in the chroot gets to run code as uid 0. GNU chroot resolves the user before chroot. The fix is the same. Static linking doesn't help; NSS is dynamic regardless.

That's eight classes, 44 CVEs, none caught by the borrow checker.

What the GNU coreutils maintainers said back

The HN thread on Endler's piece runs 361 comments deep, and the most useful single comment is the first one: a self-identified GNU coreutils maintainer disagreeing with one of Endler's rules and showing the cost.

Endler's recommended fix for path-string-equality bugs is fs::canonicalize — resolve .., ., and symlinks into a real absolute path before comparing. The maintainer's response constructs a deeply-nested directory and benchmarks both implementations:

$ mkdir -p $(yes a/ | head -n $((32 * 1024)) | tr -d '\n')
$ while cd $(yes a/ | head -n 1024 | tr -d '\n'); do :; done 2>/dev/null
$ echo a > file
$ time cp file copy
real 0m0.010s
$ time uu_cp file copy
real 0m12.857s

Enter fullscreen mode Exit fullscreen mode

GNU cp finishes in 10 milliseconds. The uutils cp, with the canonicalize-style logic, takes 12.857 seconds. That's a 1,200× slowdown on a contrived but real-shaped path. The maintainer's general point is that GNU "works very hard to avoid arbitrary limits" — the kinds of inputs that look pathological are the inputs that production shell scripts hit, and a 12-second cp in a deep build tree is its own production incident.

The same comment also lands a sharper correction on Endler's strongest claim. The post asserts "The Rust rewrite has shipped zero of these [memory-safety bugs], over a comparable window of activity." The maintainer points to GHSA-w9vv-q986-vj7x, an actual memory-safety advisory in the Rust uutils. The advisory was fixed before LTS shipped. But "zero" is now contested, and Endler's framing suffers proportionally.

The thread does not end Endler's argument. The boundary categories he lists are real. The categorical claim about memory-safety wins, however, gets dialed down from "zero, full stop" to "close to zero, with caveats." That is a smaller difference than the rewrite-vs-keep-it argument turns on, but it's the difference between a slogan and a measurement.

The structural argument from the thread

A second thread of HN testimony argues that the Rust-vs-C framing is the wrong axis entirely. As one commenter put it: "the function signature is what they read, but the scars are what they need." The GNU coreutils lineage runs from the late 1980s fileutils / textutils / shellutils packages (unified into coreutils in 2002), and decades of trickle fixes — a pwd buffer overflow on deep paths longer than 2 * PATH_MAX, an od --strings -N write past a heap buffer, a b2sum --check reading unallocated memory — encode an enormous amount of "don't do this, here's why" knowledge that lives in the codebase as scars rather than as documentation. A clean-room rewrite re-derives some of those scars on the customer's machine.

Another commenter put the same argument bluntly: "They knew how to write Rust, but clearly weren't sufficiently experienced with Unix APIs, semantics, and pitfalls. Most of those mistakes are exceedingly amateur from the perspective of long-time GNU coreutils developers." A third extended it into a process critique: "A rewrite necessarily has orders of magnitude more bugs and vulnerabilities than a decades-old well-maintained codebase, so the security argument was only valid for a long-term transition, not a rushed one."

There is a counter-argument, and Endler's piece is the strongest version of it. The Rust rewrite did not ship the memory-safety class of bug. The audit-published CVE list contains no buffer overflows, no use-after-free, no double-free, no data races on shared mutable state, no null-pointer dereferences, no uninitialized memory reads. GNU coreutils has shipped CVEs in every one of those categories in the past few years alone, by Endler's enumeration:

  • pwd buffer overflow on deep paths longer than 2 * PATH_MAX (9.11, 2026)
  • numfmt out-of-bounds read on trailing blanks (9.9, 2025)
  • unexpand --tabs heap buffer overflow (9.9, 2025)
  • od --strings -N writes a NUL byte past a heap buffer (9.8, 2025)
  • sort 1-byte read before a heap buffer with a SIZE_MAX key offset (9.8, 2025)
  • split --line-bytes heap overwrite (CVE-2024-0684, 9.5, 2024)
  • tail -f stack buffer overrun with many files and a high ulimit -n (9.0, 2021)

Whatever you think about the Canonical decision to ship uutils into a default Ubuntu install, the comparison Endler is asking you to make is genuine. The Rust rewrite's CVE list reads like a different kind of failure from the GNU CVE list. Same failure surface (privilege-bearing system tools); different class of bug.

Where the security boundary moved

Read all 44 CVEs side by side and the structural picture isn't "Rust failed to catch the bugs." It's "Rust caught the class of bug it catches, and the audit found the next class over."

The next class lives at the boundary between the Rust program and the operating system. Endler names the boundary explicitly: "It lives at the boundary between our controlled Rust environment and the messy, chaotic outside world, where paths, bytes, strings, and syscalls are all tangled up in one eternal ball of sadness. That's the new security boundary of modern systems code." The Rust standard library makes the path-of-least-resistance choice land on the wrong side of that boundary in several places — &Path parameters everywhere, String for things that are actually byte sequences, unwrap and expect available with no friction. The handle-based APIs (openat, fstatat, unlinkat) exist on every Unix; std::fs doesn't put them front and center.1

That's not a Rust-language criticism. It's a std::fs-design observation, and the design choice traces to the cross-platform constraints of Rust 1.0's standard library. As one HN commenter put it, "std::fs suffers from being a lowest common denominator. Rust had to have something at 1.0, and unfortunately it stayed like that." The same shape repeats in nearly every language with a portable filesystem abstraction; it's not unique to Rust. But the framing of the Rust rewrite — "we rewrote it in Rust, therefore it is more secure" — depends on the language doing more work than std::fs is currently set up to do at the syscall boundary.

The audit-list categories — TOCTOU, permission-set-after-create, path-string-equality, UTF-8-vs-bytes, panic-as-DoS, error discarding, GNU compatibility, resolve-before-cross — are the new audit checklist for any privilege-bearing Rust rewrite. None of them is caught by the compiler. All of them are catchable by code review by someone who has read this CVE list. The Rust safety story remains real and remains useful; it just stops at the syscall.

The shape of the next audit

This is the argument that survives both Endler's piece and the HN thread back-and-forth: we rewrote it in Rust is a coherent claim about one specific class of bug. It is not, on its own, a security claim about the rewritten tool. It is a security claim about the absence of the classes of bug Rust's type system catches. The classes the type system doesn't catch — eight of them, on display in 44 CVEs — have to be caught by something else.

Endler's checklist is the right shape for that something else. Grep your codebase for from_utf8_lossy, stray unwrap() calls, discarded Results, File::create, string comparisons against "/", mode-after-create directory operations. None of those is harder to find than a memory-safety bug. They take a different kind of audit and a different kind of reviewer — the kind whose 40 years of POSIX scars are the documentation that the upstream codebase didn't write down.

The 44 CVEs are not a verdict on Rust. They are a measurement of where the next audit should be looking. "We rewrote it in Rust" should be heard the way "we have unit tests" should be heard: a useful claim about a specific class of failure mode, with no claim attached about the ones it doesn't cover.


  1. Endler footnotes a related observation: the path-versus-handle TOCTOU class is in some ways easier to avoid in C than in Rust, because C code naturally reaches for an open file descriptor and the *at family of syscalls (openat, fstatat, unlinkat, mkdirat), and most creation syscalls take a mode argument directly. Rust's high-level std::fs APIs abstract over the file descriptor and operate on &Path values, which makes the path-based, re-resolving call the path of least resistance. The handle-based APIs exist on every Unix platform; Rust just doesn't put them front and center.