CVE-2026-35338: Root Path Canonicalization Fix for chmod --preserve-root
Summary
The patch replaces a raw string/path equality check against "/" with canonical path resolution before evaluating the --preserve-root guard in recursive chmod. This directly addresses the reported bypass using unnormalized paths such as '/../' that resolve to the filesystem root. The added regression test demonstrates the intended blocked case. Residual considerations remain around canonicalization error handling and platform/path semantics, but the change appears to fix the stated root cause for the disclosed issue.
Analysis
Vulnerability
CVE-2026-35338 describes a local path validation bypass in uutils/coreutils chmod when used recursively with --preserve-root. The vulnerable logic compared the user-supplied path directly to /:
if self.recursive && self.preserve_root && file == Path::new("/") {This check is syntactic rather than semantic. A path such as /../ is not equal to / as a raw Path, but it resolves to the same filesystem location. As a result, recursive execution could proceed despite the safety flag, enabling destructive permission changes on the root filesystem. The issue and affected context are tracked in the NVD record and CVE record.
Patch
The patch in uutils/coreutils PR #10033 changes the guard from direct path equality to canonicalized root detection:
if self.recursive && self.preserve_root && Self::is_root(file) {
fn is_root(file: impl AsRef<Path>) -> bool {
matches!(fs::canonicalize(&file), Ok(p) if p == Path::new("/"))
}This is the critical behavioral change: the code now resolves path components before deciding whether the target is root. The patch also adds a regression test for the disclosed bypass case:
#[test]
fn test_chmod_preserve_root_with_paths_that_resolve_to_root() {
new_ucmd!()
.arg("-R")
.arg("--preserve-root")
.arg("755")
.arg("/../")
.fails_with_code(1)
.stderr_contains("chmod: it is dangerous to operate recursively on '/'");
}The test is source-grounded evidence that the patch specifically targets unnormalized paths resolving to root, matching the vulnerability summary in the advisory sources.
Review
Pros
- The patch addresses the actual trust boundary failure: it validates the resolved filesystem object rather than the literal input string.
fs::canonicalizeis an appropriate primitive for collapsing.,.., and symlink traversal into a normalized absolute path before comparison.- The new regression test covers the reported exploit form
/../, which was previously missed by the raw equality check. - The fix is narrowly scoped and low-complexity, reducing the chance of unrelated behavioral regressions in
chmod.
Cons
- The helper returns
falseon canonicalization failure, which is safe for the disclosed case only if later execution paths do not create a different bypass condition. The patch snippet does not show broader error-handling semantics around inaccessible or race-prone paths. canonicalizeintroduces filesystem-dependent behavior and symlink resolution, so the exact semantics should be validated across supported platforms, especially if uutils targets non-Unix environments.- The added test covers one representative path but does not demonstrate a broader matrix of equivalent root-resolving inputs such as repeated traversal segments or symlink-mediated resolution to
/. - The reviewable snippet does not indicate whether similar preserve-root checks exist elsewhere in the codebase and might still rely on lexical path comparison.
Verdict
Root-cause.
Based on the available patch diff, the vulnerability stems from lexical comparison of a user-controlled path against / instead of comparing the canonical resolved target. Replacing file == Path::new("/") with a canonicalization-based is_root check directly fixes that root cause for the disclosed bypass pattern. The regression test aligns with the advisory description and demonstrates the intended blocked behavior. Subject to normal follow-up validation for platform semantics and adjacent call sites, this is a technically sound fix for the patched code path, consistent with the issue described by NVD and MITRE.
Recommended Labs
Try this vulnerability pattern yourself with hands-on labs.
- Path Traversal.go
Closest defensive hands-on match for this CVE’s root cause: unsafe path handling and failure to normalize/validate filesystem paths before enforcing a safety boundary. Although the CVE is in Rust coreutils, this lab trains the same secure coding habit of canonicalizing and constraining paths before sensitive file operations.
- Path Traversal II.py
Recommended as a slightly deeper follow-up because the CVE is not just simple traversal, but a validation bypass involving unnormalized paths and edge-case safety logic. This lab better reflects defence-in-depth review work: validating assumptions, handling path edge cases, and hardening file access controls.
- Unzip.go
Useful adjacent lab for learning how attackers exploit path components like '../' to escape intended directories. While framed as Zip Slip, it reinforces the same core lesson behind this chmod preserve-root bypass: never trust raw path strings, and always normalize and verify the resolved target stays within the intended boundary.