-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use openat when encountering ENAMETOOLONG #88731
Conversation
r? @m-ou-se (rust-highfive has picked a reviewer for you, use r? to override) |
IMHO this is very close to unexpected magic behaviour. Essentially |
File names which are too long (>255 bytes on many filesystems) would still result in Having a separate deep version for all the |
@joshtriplett, @cuviper, what's your opinion on this change? |
Why does this need |
Already answered on zulip, but repeating it here for the record: O_PATH is necessary on linux because open requires permission flags to be passed (this uses O_RDONLY) but that would fail when traversing exec/search-only directories. O_PATH circumvents that because it ignores the permission flags. Some other platforms have O_EXEC that could be used instead. @cuviper josh suggested I check with you whether RHEL happened to backport O_PATH support since the minimum kernel version the only thing keeping us from using it by default for |
No, that wasn't backported. However, Linux doesn't check for invalid/unknown flags in
So now we're talking about the failure of a new fallback, which means this is a corner case that didn't work before due to long paths, and will still fail (on old kernels) if it can't read. This seems acceptable to me, but should we let that |
That may be good enough to always use openat in
I lean towards passing them through since on newer kernels that do support O_PATH it'll be a genuine traversal error. And a complete lack of permissions is probably more common than -r+x. |
This comment has been minimized.
This comment has been minimized.
let's see if this also builds on the other unixes @bors try |
⌛ Trying commit c2ca4be16e5dc9c0bf006849c7b2f1d76c086806 with merge 8b572758695f4536fbe69894c98b5560ae8ff1cc... |
☀️ Try build successful - checks-actions |
It appears to work. Protip while fiddling with recursive deletion: Use a sandbox or have btrfs snapshots ready 😅 |
|
#95925 was closed so this is de facto unblocked, I guess. |
Ah yes. Though this PR was written prior to the fix for CVE-2022-21658 (which inconveniently has no associated issue...) so I'll have to go over it again to make sure it wouldn't reintroduce that. |
@rustbot author |
Oh. 😬 |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
The Miri subtree was changed cc @rust-lang/miri |
This comment has been minimized.
This comment has been minimized.
#[cfg(all(miri, any(target_os = "linux")))] | ||
use libc::open64; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit surprised that this import is only present for Miri -- that seems strange?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are two code-paths, one that uses openat and one that uses open. Miri only supports the latter, so the entire machinery around -at syscalls is configured-out under miri and the alternative is configured-in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, and there are some other import for open64
below for other targets. The entire set of cfg
s is just tricky to have an overview of. Makes sense.
FWIW the reason Miri needs special cases here as that the standard library doesn't expose |
This adds a fallback to various fs methods that handles ENAMETOOLONG by splitting the path into relative segments and then opening each segment with openat(dirfd, segment, O_PATH). Limitations: * not all fs methods are covered. the primary motivation is to get remove_dir_all working to delete a deep directory structure created by accident * requires O_PATH, so this can currently only be a fallback and not the default due to our current minimum kernel version. If you're not dealing with long paths it won't be used. * currently linux-only. this could be extended to platforms which have either O_PATH or O_EXEC but there's no CI coverage for the BSDs so I don't want to foist it on them * O(n²) performance if remove_dir_all has to use the fallback, albeit a small constant factor due to the long path segments used but ideally it should be rewritten to use openat in its recursion steps. But to do it properly we need a higher minimum kernel version.
it can remove arbitrarily-deep directory trees without exhausting the stack or file descriptor limits. The symlink attack/TOCTOU from CVE-2022-21658 that can occur when traversing the directory hierarchy more than level at a time is addressed by retracing the .. hierarchy after opening a descendant. Opening .. isn't subject to symlink attacks so we can reliably compare dev/ino numbers.
You could use cap-std for that which offers that functionality. Maybe it's not race-free on all platforms if they don't support the necessary primitives but I assume at least on the major unix-likes it would be using openat too. But yeah, we should get the basics into std. Though that'll pose a compatibility question around windows since the necessary API there isn't officially stable. |
☔ The latest upstream changes (presumably #115469) made this pull request unmergeable. Please resolve the merge conflicts. |
@the8472 |
Closing for now. This will be more easily implemented on top of ACP 259 |
This adds a fallback to various fs methods that handles ENAMETOOLONG by splitting the path into relative segments and then opening each segment with openat(dirfd, segment, O_PATH).
Limitations:
fixes #53339