Skip to content

Commit a935222

Browse files
alexcrichtonpietroalbini
authored andcommitted
Fix CVE-2022-21658 for WASI
1 parent 4c482ad commit a935222

File tree

1 file changed

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

1 file changed

+63
-8
lines changed

Diff for: library/std/src/sys/wasi/fs.rs

+63-8
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()
@@ -516,13 +528,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
516528
opts.directory(true);
517529
opts.read(true);
518530
let dir = File::open(p, &opts)?;
519-
Ok(ReadDir {
520-
cookie: Some(0),
521-
buf: vec![0; 128],
522-
offset: 0,
523-
cap: 0,
524-
inner: Arc::new(ReadDirInner { dir, root: p.to_path_buf() }),
525-
})
531+
Ok(ReadDir::new(dir, p.to_path_buf()))
526532
}
527533

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

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

0 commit comments

Comments
 (0)