Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs::read_dir iterator loops forever on same entry #50619

Closed
sharkdp opened this issue May 10, 2018 · 1 comment · Fixed by #50630
Closed

fs::read_dir iterator loops forever on same entry #50619

sharkdp opened this issue May 10, 2018 · 1 comment · Fixed by #50630
Labels
O-linux Operating system: Linux T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@sharkdp
Copy link
Contributor

sharkdp commented May 10, 2018

Bug description

There are certain directories that cause the fs::read_dir iterator (ReadDir) to loop indefinitely. While I am not sure what the exact properties of these directories are, I know that they do appear in /proc in the presence of zombie processes.

Consider the following Rust program ...

use std::env;
use std::fs;

fn main() {
    let path = env::args().nth(1).unwrap();

    for entry in fs::read_dir(path).unwrap() {
        println!("{:?}", entry);
    }
}

... and a zombie process with process id $ZOMBIE_PID (see below how to create a zombie process on purpose). Running the above program with:

cargo run -- /proc/$ZOMBIE_PID/net

results in an infinite loop, printing:

Err(Os { code: 22, kind: InvalidInput, message: "Invalid argument" })
Err(Os { code: 22, kind: InvalidInput, message: "Invalid argument" })
Err(Os { code: 22, kind: InvalidInput, message: "Invalid argument" })
...

How to create a zombie process to reproduce this?

  1. Copy the code from https://stackoverflow.com/a/25228579/704831 into a file called zombie.c
  2. Compile it gcc -o zombie zombie.c
  3. Run it: ./zombie
  4. Get the PID of the "defunct"/zombie process: ps -ef | grep '<defunct>'

Analysis

I did some debugging and I believe I found the cause of this.

When called on /proc/$ZOMBIE_PID/net, the readdir_r(3) function

int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);

returns error code 22 and - at the same time - returns NULL in *result, signalling the end of the directory stream.

If I am reading the code in the standard library correctly, this case can not be handled properly at the moment:

Code from the next function of impl Iterator for ReadDir:

loop {
if readdir64_r(self.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
return Some(Err(Error::last_os_error()))
}
if entry_ptr.is_null() {
return None
}
if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
return Some(Ok(ret))
}
}

To handle this properly (without looping forever), one would probably have to check for entry_ptr.is_null() in the first (Some(Err(...))) case as well. The result (whether or not it returned a NULL pointer) would probably have to be stored in some internal state of the iterator. On the forthcoming next call, the iterator could then return None.

Meta

> rustc --version
rustc 1.25.0 (84203cac6 2018-03-25)

> uname -s -r -v -m -p -i -o
Linux 4.16.7-1-ARCH #1 SMP PREEMPT Wed May 2 21:12:36 UTC 2018 x86_64 unknown unknown GNU/Linux
@nagisa
Copy link
Member

nagisa commented May 10, 2018

Tagging linux as we do not have O-unix tag. Bug suspected to affect all unixes.

bors added a commit that referenced this issue Jun 26, 2018
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
sharkdp added a commit to sharkdp/ripgrep that referenced this issue Sep 17, 2018
This upgrades the minimum required version of Rust to 1.29 in order to
fix BurntSushi#916 (Zombie processes cause `rg --files` to hang in `/proc`).

See also:
- Rust compiler bug ticket: rust-lang/rust#50619
- Rust compiler PR with the fix: rust-lang/rust#50630

closes BurntSushi#916
sharkdp added a commit to sharkdp/fd that referenced this issue Sep 17, 2018
This upgrades the minimum required version of Rust to 1.29 in order to
fix #288.

See also:
- Rust compiler bug ticket: rust-lang/rust#50619
- Rust compiler PR with the fix: rust-lang/rust#50630

closes #288
sharkdp added a commit to sharkdp/fd that referenced this issue Sep 18, 2018
This upgrades the minimum required version of Rust to 1.29 in order to
fix #288.

See also:
- Rust compiler bug ticket: rust-lang/rust#50619
- Rust compiler PR with the fix: rust-lang/rust#50630

closes #288
bors pushed a commit to rust-lang-ci/rust that referenced this issue Dec 18, 2022
Bug rust-lang#50619 was fixed by adding an end_of_stream flag in rust-lang#50630.
Unfortunately, that fix only applied to the readdir_r() path.  When I
switched Linux to use readdir() in rust-lang#92778, I inadvertently reintroduced
the bug on that platform.  Other platforms that had always used
readdir() were presumably never fixed.

This patch enables end_of_stream for all platforms, and adds a
Linux-specific regression test that should hopefully prevent the bug
from being reintroduced again.
bors added a commit to rust-lang-ci/rust that referenced this issue Dec 18, 2022
…imulacrum

fs: Fix rust-lang#50619 (again) and add a regression test

Bug rust-lang#50619 was fixed by adding an end_of_stream flag in rust-lang#50630.
Unfortunately, that fix only applied to the readdir_r() path.  When I
switched Linux to use readdir() in rust-lang#92778, I inadvertently reintroduced
the bug on that platform.  Other platforms that had always used
readdir() were presumably never fixed.

This patch enables end_of_stream for all platforms, and adds a
Linux-specific regression test that should hopefully prevent the bug
from being reintroduced again.
tavianator added a commit to tavianator/ripgrep that referenced this issue Dec 28, 2022
The underlying Rust issue[1] was recently re-introduced and re-fixed.
Rather than wait for a new Rust release, apply effectively the same fix
as a workaround in the ignore crate itself.

[1]: rust-lang/rust#50619
Aaron1011 pushed a commit to Aaron1011/rust that referenced this issue Jan 6, 2023
…imulacrum

fs: Fix rust-lang#50619 (again) and add a regression test

Bug rust-lang#50619 was fixed by adding an end_of_stream flag in rust-lang#50630.
Unfortunately, that fix only applied to the readdir_r() path.  When I
switched Linux to use readdir() in rust-lang#92778, I inadvertently reintroduced
the bug on that platform.  Other platforms that had always used
readdir() were presumably never fixed.

This patch enables end_of_stream for all platforms, and adds a
Linux-specific regression test that should hopefully prevent the bug
from being reintroduced again.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
O-linux Operating system: Linux T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants