diff options
| author | bors <bors@rust-lang.org> | 2022-08-31 07:57:09 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-08-31 07:57:09 +0000 |
| commit | 12e4fd0755d7d976d4ee0f2004dc938290752ff7 (patch) | |
| tree | 2a88229f6a91bb1741d22d923002d1b253346689 /library/std | |
| parent | 7f442f8ba174fd4233a14ef4d7b577aa907db594 (diff) | |
| parent | 49ed325759ca51034fb965b161a06edf9a33e73c (diff) | |
| download | rust-12e4fd0755d7d976d4ee0f2004dc938290752ff7.tar.gz rust-12e4fd0755d7d976d4ee0f2004dc938290752ff7.zip | |
Auto merge of #101225 - matthiaskrgr:rollup-9s1chas, r=matthiaskrgr
Rollup of 8 pull requests Successful merges: - #100970 (Allow deriving multipart suggestions) - #100984 (Reinstate preloading of some dll imports) - #101011 (Use getentropy when possible on all Apple platforms) - #101025 (Add tier-3 support for powerpc64 and riscv64 openbsd) - #101049 (Remove span fatal from ast lowering) - #101100 (Make call suggestions more general and more accurate) - #101171 (Fix UB from misalignment and provenance widening in `std::sys::windows`) - #101185 (Tweak `WellFormedLoc`s a bit) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'library/std')
| -rw-r--r-- | library/std/src/sys/unix/rand.rs | 94 | ||||
| -rw-r--r-- | library/std/src/sys/windows/c.rs | 9 | ||||
| -rw-r--r-- | library/std/src/sys/windows/compat.rs | 82 | ||||
| -rw-r--r-- | library/std/src/sys/windows/fs.rs | 69 | ||||
| -rw-r--r-- | library/std/src/sys/windows/mod.rs | 8 |
5 files changed, 163 insertions, 99 deletions
diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index a6fe07873d7..40885417308 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -137,11 +137,9 @@ mod imp { } } -#[cfg(target_os = "macos")] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] mod imp { - use crate::fs::File; - use crate::io::Read; - use crate::sys::os::errno; + use crate::io; use crate::sys::weak::weak; use libc::{c_int, c_void, size_t}; @@ -155,7 +153,7 @@ mod imp { for s in v.chunks_mut(256) { let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) }; if ret == -1 { - panic!("unexpected getentropy error: {}", errno()); + panic!("unexpected getentropy error: {}", io::Error::last_os_error()); } } true @@ -163,14 +161,64 @@ mod imp { .unwrap_or(false) } + #[cfg(target_os = "macos")] + fn fallback_fill_bytes(v: &mut [u8]) { + use crate::fs::File; + use crate::io::Read; + + let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom"); + file.read_exact(v).expect("failed to read /dev/urandom") + } + + // On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with + // `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded + // from `/dev/random` and which runs on its own thread accessed via GCD. + // + // This is very heavyweight compared to the alternatives, but they may not be usable: + // - `getentropy` was added in iOS 10, but we support a minimum of iOS 7 + // - `/dev/urandom` is not accessible inside the iOS app sandbox. + // + // Therefore `SecRandomCopyBytes` is only used on older iOS versions where no + // better options are present. + #[cfg(target_os = "ios")] + fn fallback_fill_bytes(v: &mut [u8]) { + use crate::ptr; + + enum SecRandom {} + + #[allow(non_upper_case_globals)] + const kSecRandomDefault: *const SecRandom = ptr::null(); + + extern "C" { + fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int; + } + + let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) }; + if ret == -1 { + panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); + } + } + + // All supported versions of watchOS (>= 5) have support for `getentropy`. + #[cfg(target_os = "watchos")] + #[cold] + fn fallback_fill_bytes(_: &mut [u8]) { + unreachable!() + } + pub fn fill_bytes(v: &mut [u8]) { if getentropy_fill_bytes(v) { return; } - // for older macos which doesn't support getentropy - let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom"); - file.read_exact(v).expect("failed to read /dev/urandom") + // Older macOS versions (< 10.12) don't support `getentropy`. Fallback to + // reading from `/dev/urandom` on these systems. + // + // Older iOS versions (< 10) don't support it either. Fallback to + // `SecRandomCopyBytes` on these systems. On watchOS, this is unreachable + // because the minimum supported version is 5 while `getentropy` became accessible + // in 3. + fallback_fill_bytes(v) } } @@ -189,36 +237,6 @@ mod imp { } } -// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with -// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded -// from `/dev/random` and which runs on its own thread accessed via GCD. -// This seems needlessly heavyweight for the purposes of generating two u64s -// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is -// only used on iOS where direct access to `/dev/urandom` is blocked by the -// sandbox. -#[cfg(any(target_os = "ios", target_os = "watchos"))] -mod imp { - use crate::io; - use crate::ptr; - use libc::{c_int, size_t}; - - enum SecRandom {} - - #[allow(non_upper_case_globals)] - const kSecRandomDefault: *const SecRandom = ptr::null(); - - extern "C" { - fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int; - } - - pub fn fill_bytes(v: &mut [u8]) { - let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) }; - if ret == -1 { - panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); - } - } -} - #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] mod imp { use crate::ptr; diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index ef3f6a9ba17..c99c8efe436 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -228,8 +228,6 @@ pub const IPV6_ADD_MEMBERSHIP: c_int = 12; pub const IPV6_DROP_MEMBERSHIP: c_int = 13; pub const MSG_PEEK: c_int = 0x2; -pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = 0x800; - #[repr(C)] #[derive(Copy, Clone)] pub struct linger { @@ -503,6 +501,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, @@ -511,6 +511,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, @@ -521,6 +523,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, @@ -1032,7 +1036,6 @@ extern "system" { pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void; pub fn GetModuleHandleA(lpModuleName: LPCSTR) -> HMODULE; pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; - pub fn LoadLibraryExA(lplibfilename: *const i8, hfile: HANDLE, dwflags: u32) -> HINSTANCE; pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME); pub fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO); diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index 9c8ddc3aa1d..7dff81ecb8d 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -21,9 +21,52 @@ use crate::ffi::{c_void, CStr}; use crate::ptr::NonNull; -use crate::sync::atomic::{AtomicBool, Ordering}; +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) => {{ @@ -75,20 +118,6 @@ impl Module { NonNull::new(module).map(Self) } - /// Load the library (if not already loaded) - /// - /// # Safety - /// - /// The module must not be unloaded. - pub unsafe fn load_system_library(name: &CStr) -> Option<Self> { - let module = c::LoadLibraryExA( - name.as_ptr(), - crate::ptr::null_mut(), - c::LOAD_LIBRARY_SEARCH_SYSTEM32, - ); - NonNull::new(module).map(Self) - } - // Try to get the address of a function. pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> { // SAFETY: @@ -182,14 +211,10 @@ macro_rules! compat_fn_optional { #[inline(always)] pub fn option() -> Option<F> { - let f = PTR.load(Ordering::Acquire); - if !f.is_null() { Some(unsafe { mem::transmute(f) }) } else { try_load() } - } - - #[cold] - fn try_load() -> Option<F> { - $load_functions; - NonNull::new(PTR.load(Ordering::Acquire)).map(|f| unsafe { mem::transmute(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) }) } } )+ @@ -205,17 +230,14 @@ pub(super) fn load_synch_functions() { // Try loading the library and all the required functions. // If any step fails, then they all fail. - let library = unsafe { Module::load_system_library(MODULE_NAME) }?; + 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::Release); - c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Release); + c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed); + c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed); Some(()) } - // Try to load the module but skip loading if a previous attempt failed. - static LOAD_MODULE: AtomicBool = AtomicBool::new(true); - let module_loaded = LOAD_MODULE.load(Ordering::Acquire) && try_load().is_some(); - LOAD_MODULE.store(module_loaded, Ordering::Release) + try_load(); } diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 9653b1abfc3..0aa7c50ded1 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -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,9 @@ 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]; + let mut b = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); if let Ok((_, buf)) = self.reparse_point(&mut b) { - reparse_tag = buf.ReparseTag; + reparse_tag = (*buf).ReparseTag; } } Ok(FileAttr { @@ -389,9 +389,9 @@ 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]; + let mut b = Align8([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); if let Ok((_, buf)) = self.reparse_point(&mut b) { - attr.reparse_tag = buf.ReparseTag; + attr.reparse_tag = (*buf).ReparseTag; } } Ok(attr) @@ -458,38 +458,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<[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([0u8; 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 +505,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, @@ -649,18 +658,18 @@ impl File { /// A buffer for holding directory entries. struct DirBuff { - buffer: Vec<u8>, + buffer: Box<Align8<[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 { buffer: Box::new(Align8([0u8; Self::BUFFER_SIZE])) } } 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<'_> { @@ -669,7 +678,7 @@ impl DirBuff { } impl AsRef<[u8]> for DirBuff { fn as_ref(&self) -> &[u8] { - &self.buffer + &self.buffer.0 } } @@ -697,9 +706,12 @@ impl<'a> Iterator for DirBuffIter<'a> { // used to get the file name slice. 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,9 +1349,10 @@ 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([0u8; 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"\??\"; @@ -1359,7 +1372,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/mod.rs b/library/std/src/sys/windows/mod.rs index a9846a48488..340cae4066b 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -329,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); |
