chmod/chown: symlink cycles detection#11805
Conversation
|
GNU testsuite comparison: |
e17c9e9 to
1990e91
Compare
|
|
168aac1 to
16ecfdd
Compare
|
@sylvestre can you please take a look? |
| @@ -403,7 +405,10 @@ impl Chmoder { | |||
| return Err(ChmodError::PreserveRoot("/".into()).into()); | |||
| } | |||
| if self.recursive { | |||
| r = self.walk_dir_with_context(file, true).and(r); | |||
| let mut ancestors = HashSet::new(); | |||
There was a problem hiding this comment.
Hi,
I'm new here and found this PR when looking at how first issues are handled. Please correct me if i'm missing something.
Looking at GNU implementation, they push down the onus of cycle detection to a generic read function and only reference a cycle detection flag. That looks modular enough to not pass a new hash set everytime?
How would this be extended extended to other places when required?
|
@sylvestre bump |
16ecfdd to
e5c9df7
Compare
|
bump² @sylvestre |
|
@cakebaker I hope it's okay to ping you as well like this |
| let mut r = Ok(()); | ||
|
|
||
| // Cycle detection: check if this directory (resolved through any symlink) is an ancestor. | ||
| if let Ok(info) = FileInformation::from_path(dir_path, true) { |
There was a problem hiding this comment.
seems duplicated from above, no ?
|
it seems that you have two stat() calls per directory (insert + remove) on top of the existing read_dir. Also, please add a "two symlinks to same dir = not a cycle" test. |
| let mut r = Ok(()); | ||
|
|
||
| // Cycle detection: check if this directory (resolved through any symlink) is an ancestor. | ||
| if let Ok(info) = FileInformation::from_path(dir_path, true) { |
There was a problem hiding this comment.
TOCTOU re-resolution in the safe-traversal path. Inside safe_traverse_dir (both chmod.rs:526 and perms.rs:461) the code already holds dir_fd, an open fd to exactly this directory - but cycle identity is computed with FileInformation::from_path(dir_path, true), which re-resolves the path through symlinks. That reintroduces the very race window safe_traversal exists to close. Use FileInformation::from_file(dir_fd) instead - it's TOCTOU-safe and avoids a redundant path walk. (DirFd: AsFd and FileInformation::from_file(&impl AsFd) both already exist.)
There was a problem hiding this comment.
Now it should be better. Please take a look.
ea423d1 to
2639aa1
Compare
|
openbsd test failing doesn't seem to have anything to do with these changes |
| } else { | ||
| result | ||
| // cSpell:disable | ||
| .stdout_contains_line("mode of 'a' retained as 0755 (rwxr-xr-x)") |
There was a problem hiding this comment.
This asserts 0755, which assumes umask 022 - set the dir mode explicitly so it doesn't fail under a different umask.
|
well done |
Attempts to fix: #11614