Skip to content

Commit 406cc07

Browse files
alexcrichtonpietroalbini
authored andcommitted
Fix CVE-2022-21658 for WASI
1 parent 32ed6e5 commit 406cc07

File tree

1 file changed

+63
-8
lines changed
  • library/std/src/sys/wasi

1 file changed

+63
-8
lines changed

library/std/src/sys/wasi/fs.rs

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::sys::time::SystemTime;
1616
use crate::sys::unsupported;
1717
use crate::sys_common::{AsInner, FromInner, IntoInner};
1818

19-
pub use crate::sys_common::fs::{remove_dir_all, try_exists};
19+
pub use crate::sys_common::fs::try_exists;
2020

2121
pub struct File {
2222
fd: WasiFd,
@@ -130,6 +130,18 @@ impl FileType {
130130
}
131131
}
132132

133+
impl ReadDir {
134+
fn new(dir: File, root: PathBuf) -> ReadDir {
135+
ReadDir {
136+
cookie: Some(0),
137+
buf: vec![0; 128],
138+
offset: 0,
139+
cap: 0,
140+
inner: Arc::new(ReadDirInner { dir, root }),
141+
}
142+
}
143+
}
144+
133145
impl fmt::Debug for ReadDir {
134146
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135147
f.debug_struct("ReadDir").finish_non_exhaustive()
@@ -512,13 +524,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
512524
opts.directory(true);
513525
opts.read(true);
514526
let dir = File::open(p, &opts)?;
515-
Ok(ReadDir {
516-
cookie: Some(0),
517-
buf: vec![0; 128],
518-
offset: 0,
519-
cap: 0,
520-
inner: Arc::new(ReadDirInner { dir, root: p.to_path_buf() }),
521-
})
527+
Ok(ReadDir::new(dir, p.to_path_buf()))
522528
}
523529

524530
pub fn unlink(p: &Path) -> io::Result<()> {
@@ -712,3 +718,52 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
712718

713719
io::copy(&mut reader, &mut writer)
714720
}
721+
722+
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
723+
let (parent, path) = open_parent(path)?;
724+
remove_dir_all_recursive(&parent, &path)
725+
}
726+
727+
fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> {
728+
// Open up a file descriptor for the directory itself. Note that we don't
729+
// follow symlinks here and we specifically open directories.
730+
//
731+
// At the root invocation of this function this will correctly handle
732+
// symlinks passed to the top-level `remove_dir_all`. At the recursive
733+
// level this will double-check that after the `readdir` call deduced this
734+
// was a directory it's still a directory by the time we open it up.
735+
//
736+
// If the opened file was actually a symlink then the symlink is deleted,
737+
// not the directory recursively.
738+
let mut opts = OpenOptions::new();
739+
opts.lookup_flags(0);
740+
opts.directory(true);
741+
opts.read(true);
742+
let fd = open_at(parent, path, &opts)?;
743+
if fd.file_attr()?.file_type().is_symlink() {
744+
return parent.unlink_file(osstr2str(path.as_ref())?);
745+
}
746+
747+
// this "root" is only used by `DirEntry::path` which we don't use below so
748+
// it's ok for this to be a bogus value
749+
let dummy_root = PathBuf::new();
750+
751+
// Iterate over all the entries in this directory, and travel recursively if
752+
// necessary
753+
for entry in ReadDir::new(fd, dummy_root) {
754+
let entry = entry?;
755+
let path = crate::str::from_utf8(&entry.name).map_err(|_| {
756+
io::Error::new_const(io::ErrorKind::Uncategorized, &"invalid utf-8 file name found")
757+
})?;
758+
759+
if entry.file_type()?.is_dir() {
760+
remove_dir_all_recursive(&entry.inner.dir.fd, path.as_ref())?;
761+
} else {
762+
entry.inner.dir.fd.unlink_file(path)?;
763+
}
764+
}
765+
766+
// Once all this directory's contents are deleted it should be safe to
767+
// delete the directory tiself.
768+
parent.remove_directory(osstr2str(path.as_ref())?)
769+
}

0 commit comments

Comments
 (0)