--- /dev/null
+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
+