diff options
Diffstat (limited to 'library/std/src/sys/windows')
-rw-r--r-- | library/std/src/sys/windows/alloc.rs | 5 | ||||
-rw-r--r-- | library/std/src/sys/windows/c.rs | 35 | ||||
-rw-r--r-- | library/std/src/sys/windows/cmath.rs | 2 | ||||
-rw-r--r-- | library/std/src/sys/windows/compat.rs | 232 | ||||
-rw-r--r-- | library/std/src/sys/windows/fs.rs | 119 | ||||
-rw-r--r-- | library/std/src/sys/windows/handle.rs | 12 | ||||
-rw-r--r-- | library/std/src/sys/windows/locks/mutex.rs | 2 | ||||
-rw-r--r-- | library/std/src/sys/windows/mod.rs | 28 | ||||
-rw-r--r-- | library/std/src/sys/windows/os.rs | 6 | ||||
-rw-r--r-- | library/std/src/sys/windows/os_str.rs | 4 | ||||
-rw-r--r-- | library/std/src/sys/windows/path/tests.rs | 2 | ||||
-rw-r--r-- | library/std/src/sys/windows/rand.rs | 121 | ||||
-rw-r--r-- | library/std/src/sys/windows/stdio.rs | 41 | ||||
-rw-r--r-- | library/std/src/sys/windows/thread_local_dtor.rs | 4 | ||||
-rw-r--r-- | library/std/src/sys/windows/thread_parker.rs | 22 |
15 files changed, 387 insertions, 248 deletions
diff --git a/library/std/src/sys/windows/alloc.rs b/library/std/src/sys/windows/alloc.rs index fdc81cdea..d53ea1600 100644 --- a/library/std/src/sys/windows/alloc.rs +++ b/library/std/src/sys/windows/alloc.rs @@ -16,6 +16,7 @@ mod tests; // Flag to indicate that the memory returned by `HeapAlloc` should be zeroed. const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008; +#[link(name = "kernel32")] extern "system" { // Get a handle to the default heap of the current process, or null if the operation fails. // @@ -168,7 +169,7 @@ unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 { // SAFETY: Because the size and alignment of a header is <= `MIN_ALIGN` and `aligned` // is aligned to at least `MIN_ALIGN` and has at least `MIN_ALIGN` bytes of padding before // it, it is safe to write a header directly before it. - unsafe { ptr::write((aligned as *mut Header).offset(-1), Header(ptr)) }; + unsafe { ptr::write((aligned as *mut Header).sub(1), Header(ptr)) }; // SAFETY: The returned pointer does not point to the to the start of an allocated block, // but there is a header readable directly before it containing the location of the start @@ -213,7 +214,7 @@ unsafe impl GlobalAlloc for System { // SAFETY: Because of the contract of `System`, `ptr` is guaranteed to be non-null // and have a header readable directly before it. - unsafe { ptr::read((ptr as *mut Header).offset(-1)).0 } + unsafe { ptr::read((ptr as *mut Header).sub(1)).0 } } }; diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 478068c73..89d0ab59b 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -66,6 +66,7 @@ pub type LPSYSTEM_INFO = *mut SYSTEM_INFO; pub type LPWSABUF = *mut WSABUF; pub type LPWSAOVERLAPPED = *mut c_void; pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = *mut c_void; +pub type BCRYPT_ALG_HANDLE = LPVOID; pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE; pub type PLARGE_INTEGER = *mut c_longlong; @@ -278,6 +279,7 @@ pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xc000000d_u32 as _; pub const STATUS_PENDING: NTSTATUS = 0x103 as _; pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _; pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _; +pub const STATUS_NOT_SUPPORTED: NTSTATUS = 0xC00000BB_u32 as _; // Equivalent to the `NT_SUCCESS` C preprocessor macro. // See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values @@ -285,7 +287,8 @@ pub fn nt_success(status: NTSTATUS) -> bool { status >= 0 } -pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD = 0x00000002; +// "RNG\0" +pub const BCRYPT_RNG_ALGORITHM: &[u16] = &[b'R' as u16, b'N' as u16, b'G' as u16, 0]; #[repr(C)] pub struct UNICODE_STRING { @@ -455,6 +458,12 @@ pub enum FILE_INFO_BY_HANDLE_CLASS { } #[repr(C)] +pub struct FILE_ATTRIBUTE_TAG_INFO { + pub FileAttributes: DWORD, + pub ReparseTag: DWORD, +} + +#[repr(C)] pub struct FILE_DISPOSITION_INFO { pub DeleteFile: BOOLEAN, } @@ -501,6 +510,8 @@ pub struct FILE_END_OF_FILE_INFO { pub EndOfFile: LARGE_INTEGER, } +/// NB: Use carefully! In general using this as a reference is likely to get the +/// provenance wrong for the `rest` field! #[repr(C)] pub struct REPARSE_DATA_BUFFER { pub ReparseTag: c_uint, @@ -509,6 +520,8 @@ pub struct REPARSE_DATA_BUFFER { pub rest: (), } +/// NB: Use carefully! In general using this as a reference is likely to get the +/// provenance wrong for the `PathBuffer` field! #[repr(C)] pub struct SYMBOLIC_LINK_REPARSE_BUFFER { pub SubstituteNameOffset: c_ushort, @@ -519,6 +532,8 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER { pub PathBuffer: WCHAR, } +/// NB: Use carefully! In general using this as a reference is likely to get the +/// provenance wrong for the `PathBuffer` field! #[repr(C)] pub struct MOUNT_POINT_REPARSE_BUFFER { pub SubstituteNameOffset: c_ushort, @@ -1217,11 +1232,18 @@ extern "system" { // >= Vista / Server 2008 // https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom pub fn BCryptGenRandom( - hAlgorithm: LPVOID, + hAlgorithm: BCRYPT_ALG_HANDLE, pBuffer: *mut u8, cbBuffer: ULONG, dwFlags: ULONG, ) -> NTSTATUS; + pub fn BCryptOpenAlgorithmProvider( + phalgorithm: *mut BCRYPT_ALG_HANDLE, + pszAlgId: LPCWSTR, + pszimplementation: LPCWSTR, + dwflags: ULONG, + ) -> NTSTATUS; + pub fn BCryptCloseAlgorithmProvider(hAlgorithm: BCRYPT_ALG_HANDLE, dwFlags: ULONG) -> NTSTATUS; } // Functions that aren't available on every version of Windows that we support, @@ -1251,17 +1273,14 @@ compat_fn_with_fallback! { } compat_fn_optional! { - pub static SYNCH_API: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0"); - - // >= Windows 8 / Server 2012 - // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitonaddress + crate::sys::compat::load_synch_functions(); pub fn WaitOnAddress( Address: LPVOID, CompareAddress: LPVOID, AddressSize: SIZE_T, dwMilliseconds: DWORD - ) -> BOOL; - pub fn WakeByAddressSingle(Address: LPVOID) -> (); + ); + pub fn WakeByAddressSingle(Address: LPVOID); } compat_fn_with_fallback! { diff --git a/library/std/src/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs index 1a5421fac..43ab8c7ee 100644 --- a/library/std/src/sys/windows/cmath.rs +++ b/library/std/src/sys/windows/cmath.rs @@ -44,7 +44,7 @@ mod shims { } // On 32-bit x86 MSVC these functions aren't defined, so we just define shims -// which promote everything fo f64, perform the calculation, and then demote +// which promote everything to f64, perform the calculation, and then demote // back to f32. While not precisely correct should be "correct enough" for now. #[cfg(all(target_env = "msvc", target_arch = "x86"))] mod shims { diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index ccc90177a..7dff81ecb 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -7,52 +7,66 @@ //! `GetModuleHandle` and `GetProcAddress` to look up DLL entry points at //! runtime. //! -//! This implementation uses a static initializer to look up the DLL entry -//! points. The CRT (C runtime) executes static initializers before `main` -//! is called (for binaries) and before `DllMain` is called (for DLLs). -//! This is the ideal time to look up DLL imports, because we are guaranteed -//! that no other threads will attempt to call these entry points. Thus, -//! we can look up the imports and store them in `static mut` fields -//! without any synchronization. +//! This is implemented simply by storing a function pointer in an atomic. +//! Loading and calling this function will have little or no overhead +//! compared with calling any other dynamically imported function. //! -//! This has an additional advantage: Because the DLL import lookup happens -//! at module initialization, the cost of these lookups is deterministic, -//! and is removed from the code paths that actually call the DLL imports. -//! That is, there is no unpredictable "cache miss" that occurs when calling -//! a DLL import. For applications that benefit from predictable delays, -//! this is a benefit. This also eliminates the comparison-and-branch -//! from the hot path. -//! -//! Currently, the standard library uses only a small number of dynamic -//! DLL imports. If this number grows substantially, then the cost of -//! performing all of the lookups at initialization time might become -//! substantial. -//! -//! The mechanism of registering a static initializer with the CRT is -//! documented in -//! [CRT Initialization](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-160). -//! It works by contributing a global symbol to the `.CRT$XCU` section. -//! The linker builds a table of all static initializer functions. -//! The CRT startup code then iterates that table, calling each -//! initializer function. -//! -//! # **WARNING!!* -//! The environment that a static initializer function runs in is highly -//! constrained. There are **many** restrictions on what static initializers -//! can safely do. Static initializer functions **MUST NOT** do any of the -//! following (this list is not comprehensive): -//! * touch any other static field that is used by a different static -//! initializer, because the order that static initializers run in -//! is not defined. -//! * call `LoadLibrary` or any other function that acquires the DLL -//! loader lock. -//! * call any Rust function or CRT function that touches any static -//! (global) state. +//! The stored function pointer starts out as an importer function which will +//! swap itself with the real function when it's called for the first time. If +//! the real function can't be imported then a fallback function is used in its +//! place. While this is low cost for the happy path (where the function is +//! already loaded) it does mean there's some overhead the first time the +//! function is called. In the worst case, multiple threads may all end up +//! importing the same function unnecessarily. use crate::ffi::{c_void, CStr}; use crate::ptr::NonNull; +use crate::sync::atomic::Ordering; use crate::sys::c; +// This uses a static initializer to preload some imported functions. +// The CRT (C runtime) executes static initializers before `main` +// is called (for binaries) and before `DllMain` is called (for DLLs). +// +// It works by contributing a global symbol to the `.CRT$XCT` section. +// The linker builds a table of all static initializer functions. +// The CRT startup code then iterates that table, calling each +// initializer function. +// +// NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer. +// If you're reading this and would like a guarantee here, please +// file an issue for discussion; currently we don't guarantee any functionality +// before main. +// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170 +#[used] +#[link_section = ".CRT$XCT"] +static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init; + +/// Preload some imported functions. +/// +/// Note that any functions included here will be unconditionally loaded in +/// the final binary, regardless of whether or not they're actually used. +/// +/// Therefore, this should be limited to `compat_fn_optional` functions which +/// must be preloaded or any functions where lazier loading demonstrates a +/// negative performance impact in practical situations. +/// +/// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`. +unsafe extern "C" fn init() { + // In an exe this code is executed before main() so is single threaded. + // In a DLL the system's loader lock will be held thereby synchronizing + // access. So the same best practices apply here as they do to running in DllMain: + // https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices + // + // DO NOT do anything interesting or complicated in this function! DO NOT call + // any Rust functions or CRT functions if those functions touch any global state, + // because this function runs during global initialization. For example, DO NOT + // do any dynamic allocation, don't call LoadLibrary, etc. + + // Attempt to preload the synch functions. + load_synch_functions(); +} + /// Helper macro for creating CStrs from literals and symbol names. macro_rules! ansi_str { (sym $ident:ident) => {{ @@ -85,39 +99,6 @@ pub(crate) const fn const_cstr_from_bytes(bytes: &'static [u8]) -> &'static CStr unsafe { crate::ffi::CStr::from_bytes_with_nul_unchecked(bytes) } } -#[used] -#[link_section = ".CRT$XCU"] -static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init; - -/// This is where the magic preloading of symbols happens. -/// -/// Note that any functions included here will be unconditionally included in -/// the final binary, regardless of whether or not they're actually used. -/// -/// Therefore, this is limited to `compat_fn_optional` functions which must be -/// preloaded and any functions which may be more time sensitive, even for the first call. -unsafe extern "C" fn init() { - // There is no locking here. This code is executed before main() is entered, and - // is guaranteed to be single-threaded. - // - // DO NOT do anything interesting or complicated in this function! DO NOT call - // any Rust functions or CRT functions if those functions touch any global state, - // because this function runs during global initialization. For example, DO NOT - // do any dynamic allocation, don't call LoadLibrary, etc. - - if let Some(synch) = Module::new(c::SYNCH_API) { - // These are optional and so we must manually attempt to load them - // before they can be used. - c::WaitOnAddress::preload(synch); - c::WakeByAddressSingle::preload(synch); - } - - if let Some(kernel32) = Module::new(c::KERNEL32) { - // Preloading this means getting a precise time will be as fast as possible. - c::GetSystemTimePreciseAsFileTime::preload(kernel32); - } -} - /// Represents a loaded module. /// /// Note that the modules std depends on must not be unloaded. @@ -151,7 +132,7 @@ impl Module { macro_rules! compat_fn_with_fallback { (pub static $module:ident: &CStr = $name:expr; $( $(#[$meta:meta])* - pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block + $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block )*) => ( pub static $module: &CStr = $name; $( @@ -196,11 +177,6 @@ macro_rules! compat_fn_with_fallback { $fallback_body } - #[allow(unused)] - pub(in crate::sys) fn preload(module: Module) { - load_from_module(Some(module)); - } - #[inline(always)] pub unsafe fn call($($argname: $argtype),*) -> $rettype { let func: F = mem::transmute(PTR.load(Ordering::Relaxed)); @@ -208,66 +184,60 @@ macro_rules! compat_fn_with_fallback { } } $(#[$meta])* - pub use $symbol::call as $symbol; + $vis use $symbol::call as $symbol; )*) } -/// A function that either exists or doesn't. +/// Optionally loaded functions. /// -/// NOTE: Optional functions must be preloaded in the `init` function above, or they will always be None. +/// Actual loading of the function defers to $load_functions. macro_rules! compat_fn_optional { - (pub static $module:ident: &CStr = $name:expr; $( - $(#[$meta:meta])* - pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty; - )*) => ( - pub static $module: &CStr = $name; + ($load_functions:expr; $( - $(#[$meta])* - pub mod $symbol { - #[allow(unused_imports)] - use super::*; - use crate::mem; - use crate::sync::atomic::{AtomicPtr, Ordering}; - use crate::sys::compat::Module; - use crate::ptr::{self, NonNull}; - - type F = unsafe extern "system" fn($($argtype),*) -> $rettype; - - /// `PTR` will either be `null()` or set to the loaded function. - static PTR: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut()); - - /// Only allow access to the function if it has loaded successfully. - #[inline(always)] - #[cfg(not(miri))] - pub fn option() -> Option<F> { - unsafe { - NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| mem::transmute(f)) + $(#[$meta:meta])* + $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) $(-> $rettype:ty)?; + )+) => ( + $( + pub mod $symbol { + use super::*; + use crate::ffi::c_void; + use crate::mem; + use crate::ptr::{self, NonNull}; + use crate::sync::atomic::{AtomicPtr, Ordering}; + + pub(in crate::sys) static PTR: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut()); + + type F = unsafe extern "system" fn($($argtype),*) $(-> $rettype)?; + + #[inline(always)] + pub fn option() -> Option<F> { + // Miri does not understand the way we do preloading + // therefore load the function here instead. + #[cfg(miri)] $load_functions; + NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) }) } } + )+ + ) +} - // Miri does not understand the way we do preloading - // therefore load the function here instead. - #[cfg(miri)] - pub fn option() -> Option<F> { - let mut func = NonNull::new(PTR.load(Ordering::Relaxed)); - if func.is_none() { - unsafe { Module::new($module).map(preload) }; - func = NonNull::new(PTR.load(Ordering::Relaxed)); - } - unsafe { - func.map(|f| mem::transmute(f)) - } - } +/// Load all needed functions from "api-ms-win-core-synch-l1-2-0". +pub(super) fn load_synch_functions() { + fn try_load() -> Option<()> { + const MODULE_NAME: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0"); + const WAIT_ON_ADDRESS: &CStr = ansi_str!("WaitOnAddress"); + const WAKE_BY_ADDRESS_SINGLE: &CStr = ansi_str!("WakeByAddressSingle"); + + // Try loading the library and all the required functions. + // If any step fails, then they all fail. + let library = unsafe { Module::new(MODULE_NAME) }?; + let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?; + let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?; + + c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed); + c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed); + Some(()) + } - #[allow(unused)] - pub(in crate::sys) fn preload(module: Module) { - unsafe { - static SYMBOL_NAME: &CStr = ansi_str!(sym $symbol); - if let Some(f) = module.proc_address(SYMBOL_NAME) { - PTR.store(f.as_ptr(), Ordering::Relaxed); - } - } - } - } - )*) + try_load(); } diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index aed082b3e..155d0297e 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -2,8 +2,8 @@ use crate::os::windows::prelude::*; use crate::ffi::OsString; use crate::fmt; -use crate::io::{self, Error, IoSlice, IoSliceMut, ReadBuf, SeekFrom}; -use crate::mem; +use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem::{self, MaybeUninit}; use crate::os::windows::io::{AsHandle, BorrowedHandle}; use crate::path::{Path, PathBuf}; use crate::ptr; @@ -11,7 +11,7 @@ use crate::slice; use crate::sync::Arc; use crate::sys::handle::Handle; use crate::sys::time::SystemTime; -use crate::sys::{c, cvt}; +use crate::sys::{c, cvt, Align8}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::thread; @@ -326,9 +326,15 @@ impl File { cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?; let mut reparse_tag = 0; if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - if let Ok((_, buf)) = self.reparse_point(&mut b) { - reparse_tag = buf.ReparseTag; + let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed(); + cvt(c::GetFileInformationByHandleEx( + self.handle.as_raw_handle(), + c::FileAttributeTagInfo, + ptr::addr_of_mut!(attr_tag).cast(), + mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(), + ))?; + if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + reparse_tag = attr_tag.ReparseTag; } } Ok(FileAttr { @@ -389,9 +395,15 @@ impl File { attr.file_size = info.AllocationSize as u64; attr.number_of_links = Some(info.NumberOfLinks); if attr.file_type().is_reparse_point() { - let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - if let Ok((_, buf)) = self.reparse_point(&mut b) { - attr.reparse_tag = buf.ReparseTag; + let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed(); + cvt(c::GetFileInformationByHandleEx( + self.handle.as_raw_handle(), + c::FileAttributeTagInfo, + ptr::addr_of_mut!(attr_tag).cast(), + mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(), + ))?; + if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + attr.reparse_tag = attr_tag.ReparseTag; } } Ok(attr) @@ -415,8 +427,8 @@ impl File { self.handle.read_at(buf, offset) } - pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> { - self.handle.read_buf(buf) + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.handle.read_buf(cursor) } pub fn write(&self, buf: &[u8]) -> io::Result<usize> { @@ -458,38 +470,46 @@ impl File { Ok(Self { handle: self.handle.try_clone()? }) } - fn reparse_point<'a>( + // NB: returned pointer is derived from `space`, and has provenance to + // match. A raw pointer is returned rather than a reference in order to + // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`. + fn reparse_point( &self, - space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE], - ) -> io::Result<(c::DWORD, &'a c::REPARSE_DATA_BUFFER)> { + space: &mut Align8<[MaybeUninit<u8>]>, + ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> { unsafe { let mut bytes = 0; cvt({ + // Grab this in advance to avoid it invalidating the pointer + // we get from `space.0.as_mut_ptr()`. + let len = space.0.len(); c::DeviceIoControl( self.handle.as_raw_handle(), c::FSCTL_GET_REPARSE_POINT, ptr::null_mut(), 0, - space.as_mut_ptr() as *mut _, - space.len() as c::DWORD, + space.0.as_mut_ptr().cast(), + len as c::DWORD, &mut bytes, ptr::null_mut(), ) })?; - Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER))) + const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8); + Ok((bytes, space.0.as_ptr().cast::<c::REPARSE_DATA_BUFFER>())) } } fn readlink(&self) -> io::Result<PathBuf> { - let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + let mut space = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); let (_bytes, buf) = self.reparse_point(&mut space)?; unsafe { - let (path_buffer, subst_off, subst_len, relative) = match buf.ReparseTag { + let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag { c::IO_REPARSE_TAG_SYMLINK => { let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER = - &buf.rest as *const _ as *const _; + ptr::addr_of!((*buf).rest).cast(); + assert!(info.is_aligned()); ( - &(*info).PathBuffer as *const _ as *const u16, + ptr::addr_of!((*info).PathBuffer).cast::<u16>(), (*info).SubstituteNameOffset / 2, (*info).SubstituteNameLength / 2, (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0, @@ -497,9 +517,10 @@ impl File { } c::IO_REPARSE_TAG_MOUNT_POINT => { let info: *const c::MOUNT_POINT_REPARSE_BUFFER = - &buf.rest as *const _ as *const _; + ptr::addr_of!((*buf).rest).cast(); + assert!(info.is_aligned()); ( - &(*info).PathBuffer as *const _ as *const u16, + ptr::addr_of!((*info).PathBuffer).cast::<u16>(), (*info).SubstituteNameOffset / 2, (*info).SubstituteNameLength / 2, false, @@ -512,7 +533,7 @@ impl File { )); } }; - let subst_ptr = path_buffer.offset(subst_off as isize); + let subst_ptr = path_buffer.add(subst_off.into()); let mut subst = slice::from_raw_parts(subst_ptr, subst_len as usize); // Absolute paths start with an NT internal namespace prefix `\??\` // We should not let it leak through. @@ -649,27 +670,31 @@ impl File { /// A buffer for holding directory entries. struct DirBuff { - buffer: Vec<u8>, + buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>, } impl DirBuff { + const BUFFER_SIZE: usize = 1024; fn new() -> Self { - const BUFFER_SIZE: usize = 1024; - Self { buffer: vec![0_u8; BUFFER_SIZE] } + Self { + // Safety: `Align8<[MaybeUninit<u8>; N]>` does not need + // initialization. + buffer: unsafe { Box::new_uninit().assume_init() }, + } } fn capacity(&self) -> usize { - self.buffer.len() + self.buffer.0.len() } fn as_mut_ptr(&mut self) -> *mut u8 { - self.buffer.as_mut_ptr().cast() + self.buffer.0.as_mut_ptr().cast() } /// Returns a `DirBuffIter`. fn iter(&self) -> DirBuffIter<'_> { DirBuffIter::new(self) } } -impl AsRef<[u8]> for DirBuff { - fn as_ref(&self) -> &[u8] { - &self.buffer +impl AsRef<[MaybeUninit<u8>]> for DirBuff { + fn as_ref(&self) -> &[MaybeUninit<u8>] { + &self.buffer.0 } } @@ -677,7 +702,7 @@ impl AsRef<[u8]> for DirBuff { /// /// Currently only returns file names (UTF-16 encoded). struct DirBuffIter<'a> { - buffer: Option<&'a [u8]>, + buffer: Option<&'a [MaybeUninit<u8>]>, cursor: usize, } impl<'a> DirBuffIter<'a> { @@ -692,14 +717,21 @@ impl<'a> Iterator for DirBuffIter<'a> { let buffer = &self.buffer?[self.cursor..]; // Get the name and next entry from the buffer. - // SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the - // last field (the file name) is unsized. So an offset has to be - // used to get the file name slice. + // SAFETY: + // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last + // field (the file name) is unsized. So an offset has to be used to + // get the file name slice. + // - The OS has guaranteed initialization of the fields of + // `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least + // `FileNameLength` bytes) let (name, is_directory, next_entry) = unsafe { let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>(); + // Guaranteed to be aligned in documentation for + // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info + assert!(info.is_aligned()); let next_entry = (*info).NextEntryOffset as usize; let name = crate::slice::from_raw_parts( - (*info).FileName.as_ptr().cast::<u16>(), + ptr::addr_of!((*info).FileName).cast::<u16>(), (*info).FileNameLength as usize / size_of::<u16>(), ); let is_directory = ((*info).FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) != 0; @@ -1337,18 +1369,19 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { let h = f.as_inner().as_raw_handle(); unsafe { - let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - let db = data.as_mut_ptr() as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER; - let buf = &mut (*db).ReparseTarget as *mut c::WCHAR; + let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + let data_ptr = data.0.as_mut_ptr(); + let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>(); + let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>(); let mut i = 0; // FIXME: this conversion is very hacky let v = br"\??\"; let v = v.iter().map(|x| *x as u16); for c in v.chain(original.as_os_str().encode_wide()) { - *buf.offset(i) = c; + *buf.add(i) = c; i += 1; } - *buf.offset(i) = 0; + *buf.add(i) = 0; i += 1; (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT; (*db).ReparseTargetMaximumLength = (i * 2) as c::WORD; @@ -1359,7 +1392,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { cvt(c::DeviceIoControl( h as *mut _, c::FSCTL_SET_REPARSE_POINT, - data.as_ptr() as *mut _, + data_ptr.cast(), (*db).ReparseDataLength + 8, ptr::null_mut(), 0, diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs index e24b09cc9..ae33d48c6 100644 --- a/library/std/src/sys/windows/handle.rs +++ b/library/std/src/sys/windows/handle.rs @@ -4,7 +4,7 @@ mod tests; use crate::cmp; -use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf}; +use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, Read}; use crate::mem; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, @@ -112,18 +112,16 @@ impl Handle { } } - pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> { - let res = unsafe { - self.synchronous_read(buf.unfilled_mut().as_mut_ptr(), buf.remaining(), None) - }; + pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + let res = + unsafe { self.synchronous_read(cursor.as_mut().as_mut_ptr(), cursor.capacity(), None) }; match res { Ok(read) => { // Safety: `read` bytes were written to the initialized portion of the buffer unsafe { - buf.assume_init(read as usize); + cursor.advance(read as usize); } - buf.add_filled(read as usize); Ok(()) } diff --git a/library/std/src/sys/windows/locks/mutex.rs b/library/std/src/sys/windows/locks/mutex.rs index f91e8f9f5..91207f5f4 100644 --- a/library/std/src/sys/windows/locks/mutex.rs +++ b/library/std/src/sys/windows/locks/mutex.rs @@ -37,8 +37,6 @@ impl Mutex { pub const fn new() -> Mutex { Mutex { srwlock: UnsafeCell::new(c::SRWLOCK_INIT) } } - #[inline] - pub unsafe fn init(&mut self) {} #[inline] pub unsafe fn lock(&self) { diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index b3f6d2d0a..eab9b9612 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -2,6 +2,7 @@ use crate::ffi::{CStr, OsStr, OsString}; use crate::io::ErrorKind; +use crate::mem::MaybeUninit; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::path::PathBuf; use crate::time::Duration; @@ -47,7 +48,7 @@ cfg_if::cfg_if! { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(_argc: isize, _argv: *const *const u8) { +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { stack_overflow::init(); // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already @@ -204,8 +205,8 @@ where // This initial size also works around `GetFullPathNameW` returning // incorrect size hints for some short paths: // https://github.com/dylni/normpath/issues/5 - let mut stack_buf = [0u16; 512]; - let mut heap_buf = Vec::new(); + let mut stack_buf: [MaybeUninit<u16>; 512] = MaybeUninit::uninit_array(); + let mut heap_buf: Vec<MaybeUninit<u16>> = Vec::new(); unsafe { let mut n = stack_buf.len(); loop { @@ -214,6 +215,11 @@ where } else { let extra = n - heap_buf.len(); heap_buf.reserve(extra); + // We used `reserve` and not `reserve_exact`, so in theory we + // may have gotten more than requested. If so, we'd like to use + // it... so long as we won't cause overflow. + n = heap_buf.capacity().min(c::DWORD::MAX as usize); + // Safety: MaybeUninit<u16> does not need initialization heap_buf.set_len(n); &mut heap_buf[..] }; @@ -228,13 +234,13 @@ where // error" is still 0 then we interpret it as a 0 length buffer and // not an actual error. c::SetLastError(0); - let k = match f1(buf.as_mut_ptr(), n as c::DWORD) { + let k = match f1(buf.as_mut_ptr().cast::<u16>(), n as c::DWORD) { 0 if c::GetLastError() == 0 => 0, 0 => return Err(crate::io::Error::last_os_error()), n => n, } as usize; if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER { - n *= 2; + n = n.saturating_mul(2).min(c::DWORD::MAX as usize); } else if k > n { n = k; } else if k == n { @@ -244,7 +250,9 @@ where // Therefore k never equals n. unreachable!(); } else { - return Ok(f2(&buf[..k])); + // Safety: First `k` values are initialized. + let slice: &[u16] = MaybeUninit::slice_assume_init_ref(&buf[..k]); + return Ok(f2(slice)); } } } @@ -321,3 +329,11 @@ pub fn abort_internal() -> ! { } crate::intrinsics::abort(); } + +/// Align the inner value to 8 bytes. +/// +/// This is enough for almost all of the buffers we're likely to work with in +/// the Windows APIs we use. +#[repr(C, align(8))] +#[derive(Copy, Clone)] +pub(crate) struct Align8<T: ?Sized>(pub T); diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs index bcac996c0..352337ba3 100644 --- a/library/std/src/sys/windows/os.rs +++ b/library/std/src/sys/windows/os.rs @@ -99,11 +99,11 @@ impl Iterator for Env { } let p = self.cur as *const u16; let mut len = 0; - while *p.offset(len) != 0 { + while *p.add(len) != 0 { len += 1; } - let s = slice::from_raw_parts(p, len as usize); - self.cur = self.cur.offset(len + 1); + let s = slice::from_raw_parts(p, len); + self.cur = self.cur.add(len + 1); // Windows allows environment variables to start with an equals // symbol (in any other position, this is the separator between diff --git a/library/std/src/sys/windows/os_str.rs b/library/std/src/sys/windows/os_str.rs index 11883f150..4bdd8c505 100644 --- a/library/std/src/sys/windows/os_str.rs +++ b/library/std/src/sys/windows/os_str.rs @@ -164,9 +164,7 @@ impl Slice { } pub fn to_owned(&self) -> Buf { - let mut buf = Wtf8Buf::with_capacity(self.inner.len()); - buf.push_wtf8(&self.inner); - Buf { inner: buf } + Buf { inner: self.inner.to_owned() } } pub fn clone_into(&self, buf: &mut Buf) { diff --git a/library/std/src/sys/windows/path/tests.rs b/library/std/src/sys/windows/path/tests.rs index 6eab38cab..623c62361 100644 --- a/library/std/src/sys/windows/path/tests.rs +++ b/library/std/src/sys/windows/path/tests.rs @@ -105,7 +105,7 @@ fn test_parse_prefix_verbatim_device() { assert_eq!(prefix, parse_prefix(r"\\?/C:\windows\system32\notepad.exe")); } -// See #93586 for more infomation. +// See #93586 for more information. #[test] fn test_windows_prefix_components() { use crate::path::Path; diff --git a/library/std/src/sys/windows/rand.rs b/library/std/src/sys/windows/rand.rs index f8fd93a73..d6cd8f802 100644 --- a/library/std/src/sys/windows/rand.rs +++ b/library/std/src/sys/windows/rand.rs @@ -1,35 +1,126 @@ -use crate::io; +//! # Random key generation +//! +//! This module wraps the RNG provided by the OS. There are a few different +//! ways to interface with the OS RNG so it's worth exploring each of the options. +//! Note that at the time of writing these all go through the (undocumented) +//! `bcryptPrimitives.dll` but they use different route to get there. +//! +//! Originally we were using [`RtlGenRandom`], however that function is +//! deprecated and warns it "may be altered or unavailable in subsequent versions". +//! +//! So we switched to [`BCryptGenRandom`] with the `BCRYPT_USE_SYSTEM_PREFERRED_RNG` +//! flag to query and find the system configured RNG. However, this change caused a small +//! but significant number of users to experience panics caused by a failure of +//! this function. See [#94098]. +//! +//! The current version changes this to use the `BCRYPT_RNG_ALG_HANDLE` +//! [Pseudo-handle], which gets the default RNG algorithm without querying the +//! system preference thus hopefully avoiding the previous issue. +//! This is only supported on Windows 10+ so a fallback is used for older versions. +//! +//! [#94098]: https://github.com/rust-lang/rust/issues/94098 +//! [`RtlGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom +//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom +//! [Pseudo-handle]: https://docs.microsoft.com/en-us/windows/win32/seccng/cng-algorithm-pseudo-handles use crate::mem; use crate::ptr; use crate::sys::c; +/// Generates high quality secure random keys for use by [`HashMap`]. +/// +/// This is used to seed the default [`RandomState`]. +/// +/// [`HashMap`]: crate::collections::HashMap +/// [`RandomState`]: crate::collections::hash_map::RandomState pub fn hashmap_random_keys() -> (u64, u64) { - let mut v = (0, 0); - let ret = unsafe { - c::BCryptGenRandom( - ptr::null_mut(), - &mut v as *mut _ as *mut u8, - mem::size_of_val(&v) as c::ULONG, - c::BCRYPT_USE_SYSTEM_PREFERRED_RNG, - ) - }; - if ret != 0 { fallback_rng() } else { v } + Rng::open().and_then(|rng| rng.gen_random_keys()).unwrap_or_else(fallback_rng) +} + +struct Rng(c::BCRYPT_ALG_HANDLE); +impl Rng { + #[cfg(miri)] + fn open() -> Result<Self, c::NTSTATUS> { + const BCRYPT_RNG_ALG_HANDLE: c::BCRYPT_ALG_HANDLE = ptr::invalid_mut(0x81); + let _ = ( + c::BCryptOpenAlgorithmProvider, + c::BCryptCloseAlgorithmProvider, + c::BCRYPT_RNG_ALGORITHM, + c::STATUS_NOT_SUPPORTED, + ); + Ok(Self(BCRYPT_RNG_ALG_HANDLE)) + } + #[cfg(not(miri))] + // Open a handle to the RNG algorithm. + fn open() -> Result<Self, c::NTSTATUS> { + use crate::sync::atomic::AtomicPtr; + use crate::sync::atomic::Ordering::{Acquire, Release}; + const ERROR_VALUE: c::LPVOID = ptr::invalid_mut(usize::MAX); + + // An atomic is used so we don't need to reopen the handle every time. + static HANDLE: AtomicPtr<crate::ffi::c_void> = AtomicPtr::new(ptr::null_mut()); + + let mut handle = HANDLE.load(Acquire); + // We use a sentinel value to designate an error occurred last time. + if handle == ERROR_VALUE { + Err(c::STATUS_NOT_SUPPORTED) + } else if handle.is_null() { + let status = unsafe { + c::BCryptOpenAlgorithmProvider( + &mut handle, + c::BCRYPT_RNG_ALGORITHM.as_ptr(), + ptr::null(), + 0, + ) + }; + if c::nt_success(status) { + // If another thread opens a handle first then use that handle instead. + let result = HANDLE.compare_exchange(ptr::null_mut(), handle, Release, Acquire); + if let Err(previous_handle) = result { + // Close our handle and return the previous one. + unsafe { c::BCryptCloseAlgorithmProvider(handle, 0) }; + handle = previous_handle; + } + Ok(Self(handle)) + } else { + HANDLE.store(ERROR_VALUE, Release); + Err(status) + } + } else { + Ok(Self(handle)) + } + } + + fn gen_random_keys(self) -> Result<(u64, u64), c::NTSTATUS> { + let mut v = (0, 0); + let status = unsafe { + let size = mem::size_of_val(&v).try_into().unwrap(); + c::BCryptGenRandom(self.0, ptr::addr_of_mut!(v).cast(), size, 0) + }; + if c::nt_success(status) { Ok(v) } else { Err(status) } + } } /// Generate random numbers using the fallback RNG function (RtlGenRandom) #[cfg(not(target_vendor = "uwp"))] #[inline(never)] -fn fallback_rng() -> (u64, u64) { +fn fallback_rng(rng_status: c::NTSTATUS) -> (u64, u64) { let mut v = (0, 0); let ret = unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) }; - if ret != 0 { v } else { panic!("fallback RNG broken: {}", io::Error::last_os_error()) } + if ret != 0 { + v + } else { + panic!( + "RNG broken: {rng_status:#x}, fallback RNG broken: {}", + crate::io::Error::last_os_error() + ) + } } /// We can't use RtlGenRandom with UWP, so there is no fallback #[cfg(target_vendor = "uwp")] #[inline(never)] -fn fallback_rng() -> (u64, u64) { - panic!("fallback RNG broken: RtlGenRandom() not supported on UWP"); +fn fallback_rng(rng_status: c::NTSTATUS) -> (u64, u64) { + panic!("RNG broken: {rng_status:#x} fallback RNG broken: RtlGenRandom() not supported on UWP"); } diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index a001d6b98..70c9b14a0 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -3,6 +3,7 @@ use crate::char::decode_utf16; use crate::cmp; use crate::io; +use crate::mem::MaybeUninit; use crate::os::windows::io::{FromRawHandle, IntoRawHandle}; use crate::ptr; use crate::str; @@ -169,13 +170,14 @@ fn write( } fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usize> { - let mut utf16 = [0u16; MAX_BUFFER_SIZE / 2]; + let mut utf16 = [MaybeUninit::<u16>::uninit(); MAX_BUFFER_SIZE / 2]; let mut len_utf16 = 0; for (chr, dest) in utf8.encode_utf16().zip(utf16.iter_mut()) { - *dest = chr; + *dest = MaybeUninit::new(chr); len_utf16 += 1; } - let utf16 = &utf16[..len_utf16]; + // Safety: We've initialized `len_utf16` values. + let utf16: &[u16] = unsafe { MaybeUninit::slice_assume_init_ref(&utf16[..len_utf16]) }; let mut written = write_u16s(handle, &utf16)?; @@ -250,11 +252,14 @@ impl io::Read for Stdin { return Ok(bytes_copied); } else if buf.len() - bytes_copied < 4 { // Not enough space to get a UTF-8 byte. We will use the incomplete UTF8. - let mut utf16_buf = [0u16; 1]; + let mut utf16_buf = [MaybeUninit::new(0); 1]; // Read one u16 character. let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, 1, &mut self.surrogate)?; // Read bytes, using the (now-empty) self.incomplete_utf8 as extra space. - let read_bytes = utf16_to_utf8(&utf16_buf[..read], &mut self.incomplete_utf8.bytes)?; + let read_bytes = utf16_to_utf8( + unsafe { MaybeUninit::slice_assume_init_ref(&utf16_buf[..read]) }, + &mut self.incomplete_utf8.bytes, + )?; // Read in the bytes from incomplete_utf8 until the buffer is full. self.incomplete_utf8.len = read_bytes as u8; @@ -262,15 +267,18 @@ impl io::Read for Stdin { bytes_copied += self.incomplete_utf8.read(&mut buf[bytes_copied..]); Ok(bytes_copied) } else { - let mut utf16_buf = [0u16; MAX_BUFFER_SIZE / 2]; + let mut utf16_buf = [MaybeUninit::<u16>::uninit(); MAX_BUFFER_SIZE / 2]; + // In the worst case, a UTF-8 string can take 3 bytes for every `u16` of a UTF-16. So // we can read at most a third of `buf.len()` chars and uphold the guarantee no data gets // lost. let amount = cmp::min(buf.len() / 3, utf16_buf.len()); let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, amount, &mut self.surrogate)?; - - match utf16_to_utf8(&utf16_buf[..read], buf) { + // Safety `read_u16s_fixup_surrogates` returns the number of items + // initialized. + let utf16s = unsafe { MaybeUninit::slice_assume_init_ref(&utf16_buf[..read]) }; + match utf16_to_utf8(utf16s, buf) { Ok(value) => return Ok(bytes_copied + value), Err(e) => return Err(e), } @@ -283,14 +291,14 @@ impl io::Read for Stdin { // This is a best effort, and might not work if we are not the only reader on Stdin. fn read_u16s_fixup_surrogates( handle: c::HANDLE, - buf: &mut [u16], + buf: &mut [MaybeUninit<u16>], mut amount: usize, surrogate: &mut u16, ) -> io::Result<usize> { // Insert possibly remaining unpaired surrogate from last read. let mut start = 0; if *surrogate != 0 { - buf[0] = *surrogate; + buf[0] = MaybeUninit::new(*surrogate); *surrogate = 0; start = 1; if amount == 1 { @@ -303,7 +311,10 @@ fn read_u16s_fixup_surrogates( let mut amount = read_u16s(handle, &mut buf[start..amount])? + start; if amount > 0 { - let last_char = buf[amount - 1]; + // Safety: The returned `amount` is the number of values initialized, + // and it is not 0, so we know that `buf[amount - 1]` have been + // initialized. + let last_char = unsafe { buf[amount - 1].assume_init() }; if last_char >= 0xD800 && last_char <= 0xDBFF { // high surrogate *surrogate = last_char; @@ -313,7 +324,8 @@ fn read_u16s_fixup_surrogates( Ok(amount) } -fn read_u16s(handle: c::HANDLE, buf: &mut [u16]) -> io::Result<usize> { +// Returns `Ok(n)` if it initialized `n` values in `buf`. +fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit<u16>]) -> io::Result<usize> { // Configure the `pInputControl` parameter to not only return on `\r\n` but also Ctrl-Z, the // traditional DOS method to indicate end of character stream / user input (SUB). // See #38274 and https://stackoverflow.com/questions/43836040/win-api-readconsole. @@ -346,8 +358,9 @@ fn read_u16s(handle: c::HANDLE, buf: &mut [u16]) -> io::Result<usize> { } break; } - - if amount > 0 && buf[amount as usize - 1] == CTRL_Z { + // Safety: if `amount > 0`, then that many bytes were written, so + // `buf[amount as usize - 1]` has been initialized. + if amount > 0 && unsafe { buf[amount as usize - 1].assume_init() } == CTRL_Z { amount -= 1; } Ok(amount as usize) diff --git a/library/std/src/sys/windows/thread_local_dtor.rs b/library/std/src/sys/windows/thread_local_dtor.rs index 25d1c6e8e..9707a95df 100644 --- a/library/std/src/sys/windows/thread_local_dtor.rs +++ b/library/std/src/sys/windows/thread_local_dtor.rs @@ -8,10 +8,14 @@ #[thread_local] static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); +// Ensure this can never be inlined because otherwise this may break in dylibs. +// See #44391. +#[inline(never)] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { DESTRUCTORS.push((t, dtor)); } +#[inline(never)] // See comment above /// Runs destructors. This should not be called until thread exit. pub unsafe fn run_keyless_dtors() { // Drop all the destructors. diff --git a/library/std/src/sys/windows/thread_parker.rs b/library/std/src/sys/windows/thread_parker.rs index d876e0f6f..2f7ae863b 100644 --- a/library/std/src/sys/windows/thread_parker.rs +++ b/library/std/src/sys/windows/thread_parker.rs @@ -197,19 +197,17 @@ impl Parker { // purpose, to make sure every unpark() has a release-acquire ordering // with park(). if self.state.swap(NOTIFIED, Release) == PARKED { - if let Some(wake_by_address_single) = c::WakeByAddressSingle::option() { - unsafe { + unsafe { + if let Some(wake_by_address_single) = c::WakeByAddressSingle::option() { wake_by_address_single(self.ptr()); - } - } else { - // If we run NtReleaseKeyedEvent before the waiting thread runs - // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up. - // If the waiting thread wakes up before we run NtReleaseKeyedEvent - // (e.g. due to a timeout), this blocks until we do wake up a thread. - // To prevent this thread from blocking indefinitely in that case, - // park_impl() will, after seeing the state set to NOTIFIED after - // waking up, call NtWaitForKeyedEvent again to unblock us. - unsafe { + } else { + // If we run NtReleaseKeyedEvent before the waiting thread runs + // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up. + // If the waiting thread wakes up before we run NtReleaseKeyedEvent + // (e.g. due to a timeout), this blocks until we do wake up a thread. + // To prevent this thread from blocking indefinitely in that case, + // park_impl() will, after seeing the state set to NOTIFIED after + // waking up, call NtWaitForKeyedEvent again to unblock us. c::NtReleaseKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut()); } } |