Skip to content

Commit

Permalink
Fix CVE-2022-21658 for WASI
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcrichton authored and pietroalbini committed Jan 19, 2022
1 parent 54e22eb commit cb748a2
Showing 1 changed file with 63 additions and 8 deletions.
71 changes: 63 additions & 8 deletions library/std/src/sys/wasi/fs.rs
Expand Up @@ -16,7 +16,7 @@ use crate::sys::time::SystemTime;
use crate::sys::unsupported;
use crate::sys_common::{AsInner, FromInner, IntoInner};

pub use crate::sys_common::fs::{remove_dir_all, try_exists};
pub use crate::sys_common::fs::try_exists;

pub struct File {
fd: WasiFd,
Expand Down Expand Up @@ -130,6 +130,18 @@ impl FileType {
}
}

impl ReadDir {
fn new(dir: File, root: PathBuf) -> ReadDir {
ReadDir {
cookie: Some(0),
buf: vec![0; 128],
offset: 0,
cap: 0,
inner: Arc::new(ReadDirInner { dir, root }),
}
}
}

impl fmt::Debug for ReadDir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReadDir").finish_non_exhaustive()
Expand Down Expand Up @@ -516,13 +528,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
opts.directory(true);
opts.read(true);
let dir = File::open(p, &opts)?;
Ok(ReadDir {
cookie: Some(0),
buf: vec![0; 128],
offset: 0,
cap: 0,
inner: Arc::new(ReadDirInner { dir, root: p.to_path_buf() }),
})
Ok(ReadDir::new(dir, p.to_path_buf()))
}

pub fn unlink(p: &Path) -> io::Result<()> {
Expand Down Expand Up @@ -716,3 +722,52 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {

io::copy(&mut reader, &mut writer)
}

pub fn remove_dir_all(path: &Path) -> io::Result<()> {
let (parent, path) = open_parent(path)?;
remove_dir_all_recursive(&parent, &path)
}

fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> {
// Open up a file descriptor for the directory itself. Note that we don't
// follow symlinks here and we specifically open directories.
//
// At the root invocation of this function this will correctly handle
// symlinks passed to the top-level `remove_dir_all`. At the recursive
// level this will double-check that after the `readdir` call deduced this
// was a directory it's still a directory by the time we open it up.
//
// If the opened file was actually a symlink then the symlink is deleted,
// not the directory recursively.
let mut opts = OpenOptions::new();
opts.lookup_flags(0);
opts.directory(true);
opts.read(true);
let fd = open_at(parent, path, &opts)?;
if fd.file_attr()?.file_type().is_symlink() {
return parent.unlink_file(osstr2str(path.as_ref())?);
}

// this "root" is only used by `DirEntry::path` which we don't use below so
// it's ok for this to be a bogus value
let dummy_root = PathBuf::new();

// Iterate over all the entries in this directory, and travel recursively if
// necessary
for entry in ReadDir::new(fd, dummy_root) {
let entry = entry?;
let path = crate::str::from_utf8(&entry.name).map_err(|_| {
io::Error::new_const(io::ErrorKind::Uncategorized, &"invalid utf-8 file name found")
})?;

if entry.file_type()?.is_dir() {
remove_dir_all_recursive(&entry.inner.dir.fd, path.as_ref())?;
} else {
entry.inner.dir.fd.unlink_file(path)?;
}
}

// Once all this directory's contents are deleted it should be safe to
// delete the directory tiself.
parent.remove_directory(osstr2str(path.as_ref())?)
}

0 comments on commit cb748a2

Please sign in to comment.