mirror of
https://git.proxmox.com/git/rustc
synced 2025-08-17 20:06:38 +00:00

backport to 1.52: - removed macos/redox impl - removed ignored test case - removed no longer needed changes to weak! (which is a v1 macro in 1.52) - used RawFd instead of OwnedFd (latter doesn't exist in 1.52) Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
264 lines
8.8 KiB
Diff
264 lines
8.8 KiB
Diff
From a03c7b755b54e09a3e274974a63c8d0ac32254d7 Mon Sep 17 00:00:00 2001
|
|
From: Hans Kratz <hans@appfour.com>
|
|
Date: Mon, 17 Jan 2022 09:45:46 +0100
|
|
Subject: [PATCH 2/4] Fix CVE-2022-21658 for UNIX-like
|
|
|
|
~~
|
|
|
|
backport to 1.52:
|
|
- removed macos/redox impl
|
|
- removed ignored test case
|
|
- removed no longer needed changes to weak! (which is a v1 macro in
|
|
1.52)
|
|
- used RawFd instead of OwnedFd (latter doesn't exist in 1.52)
|
|
|
|
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
|
|
---
|
|
library/std/src/fs/tests.rs | 70 ++++++++
|
|
library/std/src/sys/unix/fs.rs | 279 +++++++++++++++++++++++++++++--
|
|
3 files changed, 342 insertions(+), 12 deletions(-)
|
|
|
|
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
|
|
index 9a8f1e44f1f..4d1959258be 100644
|
|
--- a/library/std/src/fs/tests.rs
|
|
+++ b/library/std/src/fs/tests.rs
|
|
@@ -4,8 +4,10 @@
|
|
use crate::io::{ErrorKind, SeekFrom};
|
|
use crate::path::Path;
|
|
use crate::str;
|
|
+use crate::sync::Arc;
|
|
use crate::sys_common::io::test::{tmpdir, TempDir};
|
|
use crate::thread;
|
|
+use crate::time::{Duration, Instant};
|
|
|
|
use rand::{rngs::StdRng, RngCore, SeedableRng};
|
|
|
|
@@ -588,6 +590,21 @@ fn recursive_rmdir_of_symlink() {
|
|
}
|
|
|
|
#[test]
|
|
+fn recursive_rmdir_of_file_fails() {
|
|
+ // test we do not delete a directly specified file.
|
|
+ let tmpdir = tmpdir();
|
|
+ let canary = tmpdir.join("do_not_delete");
|
|
+ check!(check!(File::create(&canary)).write(b"foo"));
|
|
+ let result = fs::remove_dir_all(&canary);
|
|
+ #[cfg(unix)]
|
|
+ error!(result, "Not a directory");
|
|
+ #[cfg(windows)]
|
|
+ error!(result, 267); // ERROR_DIRECTORY - The directory name is invalid.
|
|
+ assert!(result.is_err());
|
|
+ assert!(canary.exists());
|
|
+}
|
|
+
|
|
+#[test]
|
|
// only Windows makes a distinction between file and directory symlinks.
|
|
#[cfg(windows)]
|
|
fn recursive_rmdir_of_file_symlink() {
|
|
#[test]
|
|
fn unicode_path_is_dir() {
|
|
assert!(Path::new(".").is_dir());
|
|
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
|
|
index a4fff9b2e64..a150d1a9aac 100644
|
|
--- a/library/std/src/sys/unix/fs.rs
|
|
+++ b/library/std/src/sys/unix/fs.rs
|
|
@@ -48,8 +48,6 @@ use libc::{
|
|
dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64,
|
|
};
|
|
|
|
-pub use crate::sys_common::fs::remove_dir_all;
|
|
-
|
|
pub struct File(FileDesc);
|
|
|
|
// FIXME: This should be available on Linux with all `target_env`.
|
|
@@ -212,7 +210,7 @@ pub struct DirEntry {
|
|
target_os = "fuchsia",
|
|
target_os = "redox"
|
|
))]
|
|
- name: Box<[u8]>,
|
|
+ name: CString,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
@@ -439,8 +437,6 @@ impl Iterator for ReadDir {
|
|
target_os = "illumos"
|
|
))]
|
|
fn next(&mut self) -> Option<io::Result<DirEntry>> {
|
|
- use crate::slice;
|
|
-
|
|
unsafe {
|
|
loop {
|
|
// Although readdir_r(3) would be a correct function to use here because
|
|
@@ -458,14 +454,10 @@ impl Iterator for ReadDir {
|
|
};
|
|
}
|
|
|
|
- let name = (*entry_ptr).d_name.as_ptr();
|
|
- let namelen = libc::strlen(name) as usize;
|
|
-
|
|
let ret = DirEntry {
|
|
entry: *entry_ptr,
|
|
- name: slice::from_raw_parts(name as *const u8, namelen as usize)
|
|
- .to_owned()
|
|
- .into_boxed_slice(),
|
|
+ // d_name is guaranteed to be null-terminated.
|
|
+ name: CStr::from_ptr((*entry_ptr).d_name.as_ptr()).to_owned(),
|
|
dir: Arc::clone(&self.inner),
|
|
};
|
|
if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
|
|
@@ -645,7 +637,26 @@ impl DirEntry {
|
|
target_os = "redox"
|
|
))]
|
|
fn name_bytes(&self) -> &[u8] {
|
|
- &*self.name
|
|
+ self.name.as_bytes()
|
|
+ }
|
|
+
|
|
+ #[cfg(not(any(
|
|
+ target_os = "solaris",
|
|
+ target_os = "illumos",
|
|
+ target_os = "fuchsia",
|
|
+ target_os = "redox"
|
|
+ )))]
|
|
+ fn name_cstr(&self) -> &CStr {
|
|
+ unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
|
|
+ }
|
|
+ #[cfg(any(
|
|
+ target_os = "solaris",
|
|
+ target_os = "illumos",
|
|
+ target_os = "fuchsia",
|
|
+ target_os = "redox"
|
|
+ ))]
|
|
+ fn name_cstr(&self) -> &CStr {
|
|
+ &self.name
|
|
}
|
|
}
|
|
|
|
@@ -1330,3 +1341,123 @@ pub fn copy(from: &Path, to: &Path) -> i
|
|
})?;
|
|
Ok(bytes_copied as u64)
|
|
}
|
|
+
|
|
+pub use remove_dir_impl::remove_dir_all;
|
|
+
|
|
+// Modern implementation using openat(), unlinkat() and fdopendir()
|
|
+#[cfg(not(any(all(target_os = "macos", target_arch = "x86_64"), target_os = "redox")))]
|
|
+mod remove_dir_impl {
|
|
+ use super::{cstr, lstat, Dir, InnerReadDir, ReadDir};
|
|
+ use crate::ffi::CStr;
|
|
+ use crate::io;
|
|
+ use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
|
|
+ use crate::os::unix::prelude::RawFd;
|
|
+ use crate::path::{Path, PathBuf};
|
|
+ use crate::sync::Arc;
|
|
+ use crate::sys::{cvt, cvt_r};
|
|
+ use libc::{fdopendir, openat, unlinkat};
|
|
+
|
|
+ pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<RawFd> {
|
|
+ let fd = cvt_r(|| unsafe {
|
|
+ openat(
|
|
+ parent_fd.unwrap_or(libc::AT_FDCWD),
|
|
+ p.as_ptr(),
|
|
+ libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY,
|
|
+ )
|
|
+ })?;
|
|
+ Ok(fd)
|
|
+ }
|
|
+
|
|
+ fn fdreaddir(dir_fd: RawFd) -> io::Result<(ReadDir, RawFd)> {
|
|
+ let ptr = unsafe { fdopendir(dir_fd.as_raw_fd()) };
|
|
+ if ptr.is_null() {
|
|
+ return Err(io::Error::last_os_error());
|
|
+ }
|
|
+ let dirp = Dir(ptr);
|
|
+ // file descriptor is automatically closed by libc::closedir() now, so give up ownership
|
|
+ let new_parent_fd = dir_fd.into_raw_fd();
|
|
+ // a valid root is not needed because we do not call any functions involving the full path
|
|
+ // of the DirEntrys.
|
|
+ let dummy_root = PathBuf::new();
|
|
+ Ok((
|
|
+ ReadDir {
|
|
+ inner: Arc::new(InnerReadDir { dirp, root: dummy_root }),
|
|
+ #[cfg(not(any(
|
|
+ target_os = "solaris",
|
|
+ target_os = "illumos",
|
|
+ target_os = "fuchsia",
|
|
+ target_os = "redox",
|
|
+ )))]
|
|
+ end_of_stream: false,
|
|
+ },
|
|
+ new_parent_fd,
|
|
+ ))
|
|
+ }
|
|
+
|
|
+ fn remove_dir_all_recursive(parent_fd: Option<RawFd>, p: &Path) -> io::Result<()> {
|
|
+ let pcstr = cstr(p)?;
|
|
+
|
|
+ // entry is expected to be a directory, open as such
|
|
+ let fd = openat_nofollow_dironly(parent_fd, &pcstr)?;
|
|
+
|
|
+ // open the directory passing ownership of the fd
|
|
+ let (dir, fd) = fdreaddir(fd)?;
|
|
+ for child in dir {
|
|
+ let child = child?;
|
|
+ let child_is_dir = if cfg!(any(
|
|
+ target_os = "solaris",
|
|
+ target_os = "illumos",
|
|
+ target_os = "haiku",
|
|
+ target_os = "vxworks"
|
|
+ )) {
|
|
+ // no d_type in dirent
|
|
+ None
|
|
+ } else {
|
|
+ match child.entry.d_type {
|
|
+ libc::DT_UNKNOWN => None,
|
|
+ libc::DT_DIR => Some(true),
|
|
+ _ => Some(false),
|
|
+ }
|
|
+ };
|
|
+ match child_is_dir {
|
|
+ Some(true) => {
|
|
+ remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?;
|
|
+ }
|
|
+ Some(false) => {
|
|
+ cvt(unsafe { unlinkat(fd, child.name_cstr().as_ptr(), 0) })?;
|
|
+ }
|
|
+ None => match cvt(unsafe { unlinkat(fd, child.name_cstr().as_ptr(), 0) }) {
|
|
+ // type unknown - try to unlink
|
|
+ Err(err)
|
|
+ if err.raw_os_error() == Some(libc::EISDIR)
|
|
+ || err.raw_os_error() == Some(libc::EPERM) =>
|
|
+ {
|
|
+ // if the file is a directory unlink fails with EISDIR on Linux and EPERM everyhwere else
|
|
+ remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?;
|
|
+ }
|
|
+ result => {
|
|
+ result?;
|
|
+ }
|
|
+ },
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // unlink the directory after removing its contents
|
|
+ cvt(unsafe {
|
|
+ unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), pcstr.as_ptr(), libc::AT_REMOVEDIR)
|
|
+ })?;
|
|
+ Ok(())
|
|
+ }
|
|
+
|
|
+ pub fn remove_dir_all(p: &Path) -> io::Result<()> {
|
|
+ // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
|
|
+ // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
|
|
+ // into symlinks.
|
|
+ let attr = lstat(p)?;
|
|
+ if attr.file_type().is_symlink() {
|
|
+ crate::fs::remove_file(p)
|
|
+ } else {
|
|
+ remove_dir_all_recursive(None, p)
|
|
+ }
|
|
+ }
|
|
+}
|
|
--
|
|
2.32.0
|
|
|