From 3e4048320dfc5d2b0178553a3e1805ac567f93e9 Mon Sep 17 00:00:00 2001 From: cerdelen Date: Sat, 3 Jan 2026 21:11:00 +0100 Subject: [PATCH 1/2] chmod: Fix --preserve-root not being bypassed by path that resolves to root --- src/uu/chmod/src/chmod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index b77de93f2b7..43760b45017 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -407,7 +407,7 @@ impl Chmoder { // should not change the permissions in this case continue; } - if self.recursive && self.preserve_root && file == Path::new("/") { + if self.recursive && self.preserve_root && Self::is_root(file) { return Err(ChmodError::PreserveRoot("/".into()).into()); } if self.recursive { @@ -419,6 +419,10 @@ impl Chmoder { r } + fn is_root(file: impl AsRef) -> bool { + matches!(fs::canonicalize(&file), Ok(p) if p == Path::new("/")) + } + #[cfg(not(target_os = "linux"))] fn walk_dir_with_context(&self, file_path: &Path, is_command_line_arg: bool) -> UResult<()> { let mut r = self.chmod_file(file_path); From 6825995b5c45a632585bbe87b3639c7d97c3fda2 Mon Sep 17 00:00:00 2001 From: cerdelen Date: Sat, 3 Jan 2026 21:11:34 +0100 Subject: [PATCH 2/2] chmod: Regression tests for --preserve-root not being bypassed by path that resolves to root --- tests/by-util/test_chmod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index 6d242020ce3..a17fc4a2ca3 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -508,6 +508,17 @@ fn test_chmod_preserve_root() { .stderr_contains("chmod: it is dangerous to operate recursively on '/'"); } +#[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 '/'"); +} + #[test] fn test_chmod_symlink_non_existing_file() { let scene = TestScenario::new(util_name!());