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

wasi/fs: Improve stopping condition for <ReadDir as Iterator>::next #133184

Merged
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions library/std/src/sys/pal/wasi/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,20 +172,22 @@ impl Iterator for ReadDir {
let offset = if self.offset == self.cap {
let cookie = self.cookie.take()?;
match self.inner.dir.fd.readdir(&mut self.buf, cookie) {
Ok(bytes) => self.cap = bytes,
Ok(bytes) => {
// No more entries if we read less than buffer size
if bytes < self.buf.len() {
self.cookie = None;
if bytes == 0 {
return None;
}
} else {
self.cookie = Some(cookie);
}
self.cap = self.buf.len();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is correct. In case readdir doesn't fill the entire buffer because there aren't many entries, this will still set the cap to the full buffer, causing us to overflow into the unfilled part and read garbage, since the data below will include it.
Or am I misunderstanding this function? It is very confusing, so I wouldn't be surprised.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right in that self.cap should be bytes. I think though we would never read unfilled part, as readdir will read up to self.buf.len(), and unfilled Vec capacity is always filled out with 0s.
But yeah, this function as a whole if kinda confusing. I've tried to change as little of it as possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, changing that condition reintroduces the original hang.. O_o

Copy link
Member

@Noratrieb Noratrieb Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the best way to fix this is to rewrite it from scratch, with some more types and enums (explicit state machine maybe?)

self.offset = 0;
0
}
Err(e) => return Some(Err(e)),
}
self.offset = 0;
self.cookie = Some(cookie);

// If we didn't actually read anything, this is in theory the
// end of the directory.
if self.cap == 0 {
self.cookie = None;
return None;
}

0
} else {
self.offset
};
Expand All @@ -197,7 +199,6 @@ impl Iterator for ReadDir {
// where we last left off.
let dirent_size = mem::size_of::<wasi::Dirent>();
if data.len() < dirent_size {
assert!(self.cookie.is_some());
assert!(self.buf.len() >= dirent_size);
self.offset = self.cap;
continue;
Expand All @@ -218,7 +219,9 @@ impl Iterator for ReadDir {
self.offset = self.cap;
continue;
}
self.cookie = Some(dirent.d_next);
self.cookie.as_mut().map(|cookie| {
*cookie = dirent.d_next;
});
self.offset = offset + dirent_size + dirent.d_namlen as usize;

let name = &data[..(dirent.d_namlen as usize)];
Expand Down
Loading