diff options
| author | bors <bors@rust-lang.org> | 2018-06-26 03:49:37 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-06-26 03:49:37 +0000 |
| commit | 7d2fa4a4d218b85c5af04981ea184cdb018cf215 (patch) | |
| tree | f4098a7b553c0258356adf518eb6f19b1ec02529 /src/libstd/sys | |
| parent | fdd9cdc8792d8fa4a64956c7d3263fa5ce18e335 (diff) | |
| parent | af75314ecdbc5564f300467e732fdb5c923a873a (diff) | |
| download | rust-7d2fa4a4d218b85c5af04981ea184cdb018cf215.tar.gz rust-7d2fa4a4d218b85c5af04981ea184cdb018cf215.zip | |
Auto merge of #50630 - sharkdp:fix-50619, r=sfackler
Fix possibly endless loop in ReadDir iterator Certain directories in `/proc` can cause the `ReadDir` iterator to loop indefinitely. We get an error code (22) when calling libc's `readdir_r` on these directories, but `entry_ptr` is `NULL` at the same time, signalling the end of the directory stream. This change introduces an internal state to the iterator such that the `Some(Err(..))` value will only be returned once when calling `next`. Subsequent calls will return `None`. fixes #50619
Diffstat (limited to 'src/libstd/sys')
| -rw-r--r-- | src/libstd/sys/unix/fs.rs | 31 |
1 files changed, 24 insertions, 7 deletions
diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 6949e22c58f..e186b115821 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -59,7 +59,10 @@ struct InnerReadDir { } #[derive(Clone)] -pub struct ReadDir(Arc<InnerReadDir>); +pub struct ReadDir { + inner: Arc<InnerReadDir>, + end_of_stream: bool, +} struct Dir(*mut libc::DIR); @@ -215,7 +218,7 @@ impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. // Thus the result will be e g 'ReadDir("/home")' - fmt::Debug::fmt(&*self.0.root, f) + fmt::Debug::fmt(&*self.inner.root, f) } } @@ -231,7 +234,7 @@ impl Iterator for ReadDir { // is safe to use in threaded applications and it is generally preferred // over the readdir_r(3C) function. super::os::set_errno(0); - let entry_ptr = libc::readdir(self.0.dirp.0); + let entry_ptr = libc::readdir(self.inner.dirp.0); if entry_ptr.is_null() { // NULL can mean either the end is reached or an error occurred. // So we had to clear errno beforehand to check for an error now. @@ -259,6 +262,10 @@ impl Iterator for ReadDir { #[cfg(not(any(target_os = "solaris", target_os = "fuchsia")))] fn next(&mut self) -> Option<io::Result<DirEntry>> { + if self.end_of_stream { + return None; + } + unsafe { let mut ret = DirEntry { entry: mem::zeroed(), @@ -266,7 +273,14 @@ impl Iterator for ReadDir { }; let mut entry_ptr = ptr::null_mut(); loop { - if readdir64_r(self.0.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { + if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { + if entry_ptr.is_null() { + // We encountered an error (which will be returned in this iteration), but + // we also reached the end of the directory stream. The `end_of_stream` + // flag is enabled to make sure that we return `None` in the next iteration + // (instead of looping forever) + self.end_of_stream = true; + } return Some(Err(Error::last_os_error())) } if entry_ptr.is_null() { @@ -289,7 +303,7 @@ impl Drop for Dir { impl DirEntry { pub fn path(&self) -> PathBuf { - self.dir.0.root.join(OsStr::from_bytes(self.name_bytes())) + self.dir.inner.root.join(OsStr::from_bytes(self.name_bytes())) } pub fn file_name(&self) -> OsString { @@ -298,7 +312,7 @@ impl DirEntry { #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] pub fn metadata(&self) -> io::Result<FileAttr> { - let fd = cvt(unsafe {dirfd(self.dir.0.dirp.0)})?; + let fd = cvt(unsafe {dirfd(self.dir.inner.dirp.0)})?; let mut stat: stat64 = unsafe { mem::zeroed() }; cvt(unsafe { fstatat64(fd, self.entry.d_name.as_ptr(), &mut stat, libc::AT_SYMLINK_NOFOLLOW) @@ -691,7 +705,10 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> { Err(Error::last_os_error()) } else { let inner = InnerReadDir { dirp: Dir(ptr), root }; - Ok(ReadDir(Arc::new(inner))) + Ok(ReadDir{ + inner: Arc::new(inner), + end_of_stream: false, + }) } } } |
