Skip to content

Commit 37fc9e2

Browse files
committed
Copy unaligned directory info
1 parent e5e79f8 commit 37fc9e2

File tree

1 file changed

+42
-28
lines changed

1 file changed

+42
-28
lines changed

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

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#![allow(nonstandard_style)]
22

33
use crate::alloc::{Layout, alloc, dealloc};
4-
use crate::borrow::Cow;
54
use crate::ffi::{OsStr, OsString, c_void};
65
use crate::fs::TryLockError;
76
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
@@ -869,7 +868,7 @@ impl DirBuff {
869868
self.buffer.0.as_mut_ptr().cast()
870869
}
871870
/// Returns a `DirBuffIter`.
872-
fn iter(&self) -> DirBuffIter<'_> {
871+
fn iter(&mut self) -> DirBuffIter<'_> {
873872
DirBuffIter::new(self)
874873
}
875874
}
@@ -883,18 +882,44 @@ impl AsRef<[MaybeUninit<u8>]> for DirBuff {
883882
///
884883
/// Currently only returns file names (UTF-16 encoded).
885884
struct DirBuffIter<'a> {
886-
buffer: Option<&'a [MaybeUninit<u8>]>,
885+
buffer: Option<&'a mut [MaybeUninit<u8>]>,
887886
cursor: usize,
888887
}
889888
impl<'a> DirBuffIter<'a> {
890-
fn new(buffer: &'a DirBuff) -> Self {
891-
Self { buffer: Some(buffer.as_ref()), cursor: 0 }
889+
fn new(buffer: &'a mut DirBuff) -> Self {
890+
Self { buffer: Some(&mut buffer.buffer.0), cursor: 0 }
892891
}
893892
}
893+
894894
impl<'a> Iterator for DirBuffIter<'a> {
895-
type Item = (Cow<'a, [u16]>, bool);
895+
type Item = (&'a [u16], bool);
896896
fn next(&mut self) -> Option<Self::Item> {
897-
let buffer = &self.buffer?[self.cursor..];
897+
let buffer = self.buffer.as_mut()?;
898+
899+
// While the file information is guaranteed to be aligned in documentation for
900+
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
901+
// it does not seem that reality is so kind.
902+
// In some cases crashes could be caused by security software that hooks system APIs
903+
// but doesn't uphold the documented alignment.
904+
// See https://github.com/rust-lang/rust/issues/104530
905+
debug_assert_eq!(self.cursor % 8, 0);
906+
let buffer = if !self.cursor.is_multiple_of(8) {
907+
// Copy the data to the start of the buffer, which we've guarenteed to be aligned.
908+
// SAFETY: the FILE_ID_BOTH_DIR_INFO struct has been correctly initialized (if not aligned).
909+
unsafe {
910+
let info = buffer.as_ptr().byte_add(self.cursor).cast::<c::FILE_ID_BOTH_DIR_INFO>();
911+
let name_offset = mem::offset_of!(c::FILE_ID_BOTH_DIR_INFO, FileName);
912+
let name_length = (&raw const (*info).FileNameLength).read_unaligned() as usize;
913+
ptr::copy(
914+
info.cast::<u8>(),
915+
buffer.as_mut_ptr().cast::<u8>(),
916+
name_offset + name_length,
917+
);
918+
}
919+
&buffer[..]
920+
} else {
921+
&buffer[self.cursor..]
922+
};
898923

899924
// Get the name and next entry from the buffer.
900925
// SAFETY:
@@ -905,17 +930,16 @@ impl<'a> Iterator for DirBuffIter<'a> {
905930
// `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
906931
// `FileNameLength` bytes)
907932
let (name, is_directory, next_entry) = unsafe {
908-
let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
909-
// While this is guaranteed to be aligned in documentation for
910-
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
911-
// it does not seem that reality is so kind, and assuming this
912-
// caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530)
913-
// presumably, this can be blamed on buggy filesystem drivers, but who knows.
914-
let next_entry = (&raw const (*info).NextEntryOffset).read_unaligned() as usize;
915-
let length = (&raw const (*info).FileNameLength).read_unaligned() as usize;
916-
let attrs = (&raw const (*info).FileAttributes).read_unaligned();
917-
let name = from_maybe_unaligned(
918-
(&raw const (*info).FileName).cast::<u16>(),
933+
let info_ptr = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
934+
// WARNING: use `info_ptr` to access the variable length FileName field.
935+
// This reference only spans the header and first item of the FileName array.
936+
let info = &*info_ptr;
937+
938+
let next_entry = info.NextEntryOffset as usize;
939+
let length = info.FileNameLength as usize;
940+
let attrs = info.FileAttributes;
941+
let name = slice::from_raw_parts(
942+
(&raw const (*info_ptr).FileName).cast::<u16>(),
919943
length / size_of::<u16>(),
920944
);
921945
let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
@@ -938,16 +962,6 @@ impl<'a> Iterator for DirBuffIter<'a> {
938962
}
939963
}
940964

941-
unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> {
942-
unsafe {
943-
if p.is_aligned() {
944-
Cow::Borrowed(crate::slice::from_raw_parts(p, len))
945-
} else {
946-
Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect())
947-
}
948-
}
949-
}
950-
951965
impl AsInner<Handle> for File {
952966
#[inline]
953967
fn as_inner(&self) -> &Handle {

0 commit comments

Comments
 (0)