diff options
Diffstat (limited to 'library/std/src')
87 files changed, 2414 insertions, 1243 deletions
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index be173a7ac..4d109285d 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -24,7 +24,7 @@ use crate::sys; /// reasonable best-effort is made to generate this seed from a high quality, /// secure source of randomness provided by the host without blocking the /// program. Because of this, the randomness of the seed depends on the output -/// quality of the system's random number generator when the seed is created. +/// quality of the system's random number coroutine when the seed is created. /// In particular, seeds generated when the system's entropy pool is abnormally /// low such as during system boot may be of a lower quality. /// diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 6d85b26af..6a87f6e5f 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -144,7 +144,7 @@ impl<T> HashSet<T, RandomState> { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(capacity: usize) -> HashSet<T, RandomState> { - HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, Default::default()) } + HashSet::with_capacity_and_hasher(capacity, Default::default()) } } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 73cce35ac..4310e1083 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -184,11 +184,12 @@ pub struct DirEntry(fs_imp::DirEntry); /// ``` #[derive(Clone, Debug)] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "FsOpenOptions")] pub struct OpenOptions(fs_imp::OpenOptions); /// Representation of the various timestamps on a file. #[derive(Copy, Clone, Debug, Default)] -#[unstable(feature = "file_set_times", issue = "98245")] +#[stable(feature = "file_set_times", since = "1.75.0")] pub struct FileTimes(fs_imp::FileTimes); /// Representation of the various permissions on a file. @@ -201,6 +202,7 @@ pub struct FileTimes(fs_imp::FileTimes); /// [`PermissionsExt`]: crate::os::unix::fs::PermissionsExt #[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "FsPermissions")] pub struct Permissions(fs_imp::FilePermissions); /// A structure representing a type of file with accessors for each file type. @@ -674,8 +676,6 @@ impl File { /// # Examples /// /// ```no_run - /// #![feature(file_set_times)] - /// /// fn main() -> std::io::Result<()> { /// use std::fs::{self, File, FileTimes}; /// @@ -688,7 +688,7 @@ impl File { /// Ok(()) /// } /// ``` - #[unstable(feature = "file_set_times", issue = "98245")] + #[stable(feature = "file_set_times", since = "1.75.0")] #[doc(alias = "futimens")] #[doc(alias = "futimes")] #[doc(alias = "SetFileTime")] @@ -699,7 +699,7 @@ impl File { /// Changes the modification time of the underlying file. /// /// This is an alias for `set_times(FileTimes::new().set_modified(time))`. - #[unstable(feature = "file_set_times", issue = "98245")] + #[stable(feature = "file_set_times", since = "1.75.0")] #[inline] pub fn set_modified(&self, time: SystemTime) -> io::Result<()> { self.set_times(FileTimes::new().set_modified(time)) @@ -1413,20 +1413,20 @@ impl FileTimes { /// Create a new `FileTimes` with no times set. /// /// Using the resulting `FileTimes` in [`File::set_times`] will not modify any timestamps. - #[unstable(feature = "file_set_times", issue = "98245")] + #[stable(feature = "file_set_times", since = "1.75.0")] pub fn new() -> Self { Self::default() } /// Set the last access time of a file. - #[unstable(feature = "file_set_times", issue = "98245")] + #[stable(feature = "file_set_times", since = "1.75.0")] pub fn set_accessed(mut self, t: SystemTime) -> Self { self.0.set_accessed(t.into_inner()); self } /// Set the last modified time of a file. - #[unstable(feature = "file_set_times", issue = "98245")] + #[stable(feature = "file_set_times", since = "1.75.0")] pub fn set_modified(mut self, t: SystemTime) -> Self { self.0.set_modified(t.into_inner()); self @@ -1440,7 +1440,7 @@ impl AsInnerMut<fs_imp::FileTimes> for FileTimes { } // For implementing OS extension traits in `std::os` -#[unstable(feature = "file_set_times", issue = "98245")] +#[stable(feature = "file_set_times", since = "1.75.0")] impl Sealed for FileTimes {} impl Permissions { @@ -2241,6 +2241,7 @@ pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { /// ``` #[doc(alias = "mkdir")] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "fs_create_dir")] pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { DirBuilder::new().create(path.as_ref()) } diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index d74f0f00e..547a7b705 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1707,3 +1707,89 @@ fn test_file_times() { assert_eq!(metadata.created().unwrap(), created); } } + +#[test] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +fn test_file_times_pre_epoch_with_nanos() { + #[cfg(target_os = "ios")] + use crate::os::ios::fs::FileTimesExt; + #[cfg(target_os = "macos")] + use crate::os::macos::fs::FileTimesExt; + #[cfg(target_os = "tvos")] + use crate::os::tvos::fs::FileTimesExt; + #[cfg(target_os = "watchos")] + use crate::os::watchos::fs::FileTimesExt; + + let tmp = tmpdir(); + let file = File::create(tmp.join("foo")).unwrap(); + + for (accessed, modified, created) in [ + // The first round is to set filetimes to something we know works, but this time + // it's validated with nanoseconds as well which probe the numeric boundary. + ( + SystemTime::UNIX_EPOCH + Duration::new(12345, 1), + SystemTime::UNIX_EPOCH + Duration::new(54321, 100_000_000), + SystemTime::UNIX_EPOCH + Duration::new(32123, 999_999_999), + ), + // The second rounds uses pre-epoch dates along with nanoseconds that probe + // the numeric boundary. + ( + SystemTime::UNIX_EPOCH - Duration::new(1, 1), + SystemTime::UNIX_EPOCH - Duration::new(60, 100_000_000), + SystemTime::UNIX_EPOCH - Duration::new(3600, 999_999_999), + ), + ] { + let mut times = FileTimes::new(); + times = times.set_accessed(accessed).set_modified(modified).set_created(created); + file.set_times(times).unwrap(); + + let metadata = file.metadata().unwrap(); + assert_eq!(metadata.accessed().unwrap(), accessed); + assert_eq!(metadata.modified().unwrap(), modified); + assert_eq!(metadata.created().unwrap(), created); + } +} + +#[test] +#[cfg(windows)] +fn windows_unix_socket_exists() { + use crate::sys::{c, net}; + use crate::{mem, ptr}; + + let tmp = tmpdir(); + let socket_path = tmp.join("socket"); + + // std doesn't currently support Unix sockets on Windows so manually create one here. + net::init(); + unsafe { + let socket = c::WSASocketW( + c::AF_UNIX as i32, + c::SOCK_STREAM, + 0, + ptr::null_mut(), + 0, + c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT, + ); + // AF_UNIX is not supported on earlier versions of Windows, + // so skip this test if it's unsupported and we're not in CI. + if socket == c::INVALID_SOCKET { + let error = c::WSAGetLastError(); + if env::var_os("CI").is_none() && error == c::WSAEAFNOSUPPORT { + return; + } else { + panic!("Creating AF_UNIX socket failed (OS error {error})"); + } + } + let mut addr = c::SOCKADDR_UN { sun_family: c::AF_UNIX, sun_path: mem::zeroed() }; + let bytes = socket_path.as_os_str().as_encoded_bytes(); + addr.sun_path[..bytes.len()].copy_from_slice(bytes); + let len = mem::size_of_val(&addr) as i32; + let result = c::bind(socket, ptr::addr_of!(addr).cast::<c::SOCKADDR>(), len); + c::closesocket(socket); + assert_eq!(result, 0); + } + // Make sure all ways of testing a file exist work for a Unix socket. + assert_eq!(socket_path.exists(), true); + assert_eq!(socket_path.try_exists().unwrap(), true); + assert_eq!(socket_path.metadata().is_ok(), true); +} diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 7097dfef8..55aafc3db 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -2,7 +2,8 @@ mod buffer; use crate::fmt; use crate::io::{ - self, BorrowedCursor, BufRead, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE, + self, uninlined_slow_read_byte, BorrowedCursor, BufRead, IoSliceMut, Read, Seek, SeekFrom, + SizeHint, SpecReadByte, DEFAULT_BUF_SIZE, }; use buffer::Buffer; @@ -259,6 +260,22 @@ impl<R: ?Sized + Seek> BufReader<R> { } } +impl<R> SpecReadByte for BufReader<R> +where + Self: Read, +{ + #[inline] + fn spec_read_byte(&mut self) -> Option<io::Result<u8>> { + let mut byte = 0; + if self.buf.consume_with(1, |claimed| byte = claimed[0]) { + return Some(Ok(byte)); + } + + // Fallback case, only reached once per buffer refill. + uninlined_slow_read_byte(self) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<R: ?Sized + Read> Read for BufReader<R> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { @@ -269,10 +286,8 @@ impl<R: ?Sized + Read> Read for BufReader<R> { self.discard_buffer(); return self.inner.read(buf); } - let nread = { - let mut rem = self.fill_buf()?; - rem.read(buf)? - }; + let mut rem = self.fill_buf()?; + let nread = rem.read(buf)?; self.consume(nread); Ok(nread) } diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index eafd078a7..4d51a719f 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,5 +1,7 @@ use super::{BorrowedBuf, BufReader, BufWriter, Read, Result, Write, DEFAULT_BUF_SIZE}; use crate::alloc::Allocator; +use crate::cmp; +use crate::cmp::min; use crate::collections::VecDeque; use crate::io::IoSlice; use crate::mem::MaybeUninit; @@ -254,6 +256,78 @@ impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> { } } +impl<A: Allocator> BufferedWriterSpec for Vec<u8, A> { + fn buffer_size(&self) -> usize { + cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len()) + } + + fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> { + let mut bytes = 0; + + // avoid inflating empty/small vecs before we have determined that there's anything to read + if self.capacity() < DEFAULT_BUF_SIZE { + let stack_read_limit = DEFAULT_BUF_SIZE as u64; + bytes = stack_buffer_copy(&mut reader.take(stack_read_limit), self)?; + // fewer bytes than requested -> EOF reached + if bytes < stack_read_limit { + return Ok(bytes); + } + } + + // don't immediately offer the vec's whole spare capacity, otherwise + // we might have to fully initialize it if the reader doesn't have a custom read_buf() impl + let mut max_read_size = DEFAULT_BUF_SIZE; + + loop { + self.reserve(DEFAULT_BUF_SIZE); + let mut initialized_spare_capacity = 0; + + loop { + let buf = self.spare_capacity_mut(); + let read_size = min(max_read_size, buf.len()); + let mut buf = BorrowedBuf::from(&mut buf[..read_size]); + // SAFETY: init is either 0 or the init_len from the previous iteration. + unsafe { + buf.set_init(initialized_spare_capacity); + } + match reader.read_buf(buf.unfilled()) { + Ok(()) => { + let bytes_read = buf.len(); + + // EOF + if bytes_read == 0 { + return Ok(bytes); + } + + // the reader is returning short reads but it doesn't call ensure_init() + if buf.init_len() < buf.capacity() { + max_read_size = usize::MAX; + } + // the reader hasn't returned short reads so far + if bytes_read == buf.capacity() { + max_read_size *= 2; + } + + initialized_spare_capacity = buf.init_len() - bytes_read; + bytes += bytes_read as u64; + // SAFETY: BorrowedBuf guarantees all of its filled bytes are init + // and the number of read bytes can't exceed the spare capacity since + // that's what the buffer is borrowing from. + unsafe { self.set_len(self.len() + bytes_read) }; + + // spare capacity full, reserve more + if self.len() == self.capacity() { + break; + } + } + Err(e) if e.is_interrupted() => continue, + Err(e) => return Err(e), + } + } + } + } +} + fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>( reader: &mut R, writer: &mut W, diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index d9998e87c..af137eaf8 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -81,6 +81,18 @@ fn copy_specializes_bufreader() { } #[test] +fn copy_specializes_to_vec() { + let cap = 123456; + let mut source = ShortReader { cap, observed_buffer: 0, read_size: 1337 }; + let mut sink = Vec::new(); + assert_eq!(cap as u64, io::copy(&mut source, &mut sink).unwrap()); + assert!( + source.observed_buffer > DEFAULT_BUF_SIZE, + "expected a large buffer to be provided to the reader" + ); +} + +#[test] fn copy_specializes_from_vecdeque() { let mut source = VecDeque::with_capacity(100 * 1024); for _ in 0..20 * 1024 { diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index a7428776d..d8c8d933e 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -475,6 +475,24 @@ impl<A: Allocator> Read for VecDeque<u8, A> { } } +/// BufRead is implemented for `VecDeque<u8>` by reading bytes from the front of the `VecDeque`. +#[stable(feature = "vecdeque_buf_read", since = "1.75.0")] +impl<A: Allocator> BufRead for VecDeque<u8, A> { + /// Returns the contents of the "front" slice as returned by + /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are + /// discontiguous, multiple calls to `fill_buf` will be needed to read the entire content. + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + let (front, _) = self.as_slices(); + Ok(front) + } + + #[inline] + fn consume(&mut self, amt: usize) { + self.drain(..amt); + } +} + /// Write is implemented for `VecDeque<u8>` by appending to the `VecDeque`, growing it as needed. #[stable(feature = "vecdeque_read_write", since = "1.63.0")] impl<A: Allocator> Write for VecDeque<u8, A> { @@ -510,3 +528,17 @@ impl<A: Allocator> Write for VecDeque<u8, A> { Ok(()) } } + +#[unstable(feature = "read_buf", issue = "78485")] +impl<'a> io::Write for core::io::BorrowedCursor<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + let amt = cmp::min(buf.len(), self.capacity()); + self.append(&buf[..amt]); + Ok(amt) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 604b795cd..7d70a0bac 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -317,6 +317,7 @@ pub use self::stdio::set_output_capture; #[stable(feature = "is_terminal", since = "1.70.0")] pub use self::stdio::IsTerminal; #[unstable(feature = "print_internals", issue = "none")] +#[doc(hidden)] pub use self::stdio::{_eprint, _print}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::{ @@ -329,7 +330,7 @@ pub use self::{ }; #[unstable(feature = "read_buf", issue = "78485")] -pub use self::readbuf::{BorrowedBuf, BorrowedCursor}; +pub use core::io::{BorrowedBuf, BorrowedCursor}; pub(crate) use error::const_io_error; mod buffered; @@ -338,7 +339,6 @@ mod cursor; mod error; mod impls; pub mod prelude; -mod readbuf; mod stdio; mod util; @@ -513,8 +513,7 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [ match this.read(buf) { Ok(0) => break, Ok(n) => { - let tmp = buf; - buf = &mut tmp[n..]; + buf = &mut buf[n..]; } Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), @@ -1141,10 +1140,10 @@ pub fn read_to_string<R: Read>(mut reader: R) -> Result<String> { #[repr(transparent)] pub struct IoSliceMut<'a>(sys::io::IoSliceMut<'a>); -#[stable(feature = "iovec-send-sync", since = "1.44.0")] +#[stable(feature = "iovec_send_sync", since = "1.44.0")] unsafe impl<'a> Send for IoSliceMut<'a> {} -#[stable(feature = "iovec-send-sync", since = "1.44.0")] +#[stable(feature = "iovec_send_sync", since = "1.44.0")] unsafe impl<'a> Sync for IoSliceMut<'a> {} #[stable(feature = "iovec", since = "1.36.0")] @@ -1284,10 +1283,10 @@ impl<'a> DerefMut for IoSliceMut<'a> { #[repr(transparent)] pub struct IoSlice<'a>(sys::io::IoSlice<'a>); -#[stable(feature = "iovec-send-sync", since = "1.44.0")] +#[stable(feature = "iovec_send_sync", since = "1.44.0")] unsafe impl<'a> Send for IoSlice<'a> {} -#[stable(feature = "iovec-send-sync", since = "1.44.0")] +#[stable(feature = "iovec_send_sync", since = "1.44.0")] unsafe impl<'a> Sync for IoSlice<'a> {} #[stable(feature = "iovec", since = "1.36.0")] @@ -1830,6 +1829,7 @@ pub trait Write { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoSeek")] pub trait Seek { /// Seek to an offset, in bytes, in a stream. /// @@ -2777,23 +2777,55 @@ pub struct Bytes<R> { impl<R: Read> Iterator for Bytes<R> { type Item = Result<u8>; + // Not `#[inline]`. This function gets inlined even without it, but having + // the inline annotation can result in worse code generation. See #116785. fn next(&mut self) -> Option<Result<u8>> { - let mut byte = 0; - loop { - return match self.inner.read(slice::from_mut(&mut byte)) { - Ok(0) => None, - Ok(..) => Some(Ok(byte)), - Err(ref e) if e.is_interrupted() => continue, - Err(e) => Some(Err(e)), - }; - } + SpecReadByte::spec_read_byte(&mut self.inner) } + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { SizeHint::size_hint(&self.inner) } } +/// For the specialization of `Bytes::next`. +trait SpecReadByte { + fn spec_read_byte(&mut self) -> Option<Result<u8>>; +} + +impl<R> SpecReadByte for R +where + Self: Read, +{ + #[inline] + default fn spec_read_byte(&mut self) -> Option<Result<u8>> { + inlined_slow_read_byte(self) + } +} + +/// Read a single byte in a slow, generic way. This is used by the default +/// `spec_read_byte`. +#[inline] +fn inlined_slow_read_byte<R: Read>(reader: &mut R) -> Option<Result<u8>> { + let mut byte = 0; + loop { + return match reader.read(slice::from_mut(&mut byte)) { + Ok(0) => None, + Ok(..) => Some(Ok(byte)), + Err(ref e) if e.is_interrupted() => continue, + Err(e) => Some(Err(e)), + }; + } +} + +// Used by `BufReader::spec_read_byte`, for which the `inline(ever)` is +// important. +#[inline(never)] +fn uninlined_slow_read_byte<R: Read>(reader: &mut R) -> Option<Result<u8>> { + inlined_slow_read_byte(reader) +} + trait SizeHint { fn lower_bound(&self) -> usize; @@ -2893,6 +2925,7 @@ impl<B: BufRead> Iterator for Split<B> { /// [`lines`]: BufRead::lines #[stable(feature = "rust1", since = "1.0.0")] #[derive(Debug)] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoLines")] pub struct Lines<B> { buf: B, } diff --git a/library/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs deleted file mode 100644 index 034ddd8df..000000000 --- a/library/std/src/io/readbuf.rs +++ /dev/null @@ -1,317 +0,0 @@ -#![unstable(feature = "read_buf", issue = "78485")] - -#[cfg(test)] -mod tests; - -use crate::fmt::{self, Debug, Formatter}; -use crate::io::{Result, Write}; -use crate::mem::{self, MaybeUninit}; -use crate::{cmp, ptr}; - -/// A borrowed byte buffer which is incrementally filled and initialized. -/// -/// This type is a sort of "double cursor". It tracks three regions in the buffer: a region at the beginning of the -/// buffer that has been logically filled with data, a region that has been initialized at some point but not yet -/// logically filled, and a region at the end that is fully uninitialized. The filled region is guaranteed to be a -/// subset of the initialized region. -/// -/// In summary, the contents of the buffer can be visualized as: -/// ```not_rust -/// [ capacity ] -/// [ filled | unfilled ] -/// [ initialized | uninitialized ] -/// ``` -/// -/// A `BorrowedBuf` is created around some existing data (or capacity for data) via a unique reference -/// (`&mut`). The `BorrowedBuf` can be configured (e.g., using `clear` or `set_init`), but cannot be -/// directly written. To write into the buffer, use `unfilled` to create a `BorrowedCursor`. The cursor -/// has write-only access to the unfilled portion of the buffer (you can think of it as a -/// write-only iterator). -/// -/// The lifetime `'data` is a bound on the lifetime of the underlying data. -pub struct BorrowedBuf<'data> { - /// The buffer's underlying data. - buf: &'data mut [MaybeUninit<u8>], - /// The length of `self.buf` which is known to be filled. - filled: usize, - /// The length of `self.buf` which is known to be initialized. - init: usize, -} - -impl Debug for BorrowedBuf<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("BorrowedBuf") - .field("init", &self.init) - .field("filled", &self.filled) - .field("capacity", &self.capacity()) - .finish() - } -} - -/// Create a new `BorrowedBuf` from a fully initialized slice. -impl<'data> From<&'data mut [u8]> for BorrowedBuf<'data> { - #[inline] - fn from(slice: &'data mut [u8]) -> BorrowedBuf<'data> { - let len = slice.len(); - - BorrowedBuf { - // SAFETY: initialized data never becoming uninitialized is an invariant of BorrowedBuf - buf: unsafe { (slice as *mut [u8]).as_uninit_slice_mut().unwrap() }, - filled: 0, - init: len, - } - } -} - -/// Create a new `BorrowedBuf` from an uninitialized buffer. -/// -/// Use `set_init` if part of the buffer is known to be already initialized. -impl<'data> From<&'data mut [MaybeUninit<u8>]> for BorrowedBuf<'data> { - #[inline] - fn from(buf: &'data mut [MaybeUninit<u8>]) -> BorrowedBuf<'data> { - BorrowedBuf { buf, filled: 0, init: 0 } - } -} - -impl<'data> BorrowedBuf<'data> { - /// Returns the total capacity of the buffer. - #[inline] - pub fn capacity(&self) -> usize { - self.buf.len() - } - - /// Returns the length of the filled part of the buffer. - #[inline] - pub fn len(&self) -> usize { - self.filled - } - - /// Returns the length of the initialized part of the buffer. - #[inline] - pub fn init_len(&self) -> usize { - self.init - } - - /// Returns a shared reference to the filled portion of the buffer. - #[inline] - pub fn filled(&self) -> &[u8] { - // SAFETY: We only slice the filled part of the buffer, which is always valid - unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[0..self.filled]) } - } - - /// Returns a mutable reference to the filled portion of the buffer. - #[inline] - pub fn filled_mut(&mut self) -> &mut [u8] { - // SAFETY: We only slice the filled part of the buffer, which is always valid - unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[0..self.filled]) } - } - - /// Returns a cursor over the unfilled part of the buffer. - #[inline] - pub fn unfilled<'this>(&'this mut self) -> BorrowedCursor<'this> { - BorrowedCursor { - start: self.filled, - // SAFETY: we never assign into `BorrowedCursor::buf`, so treating its - // lifetime covariantly is safe. - buf: unsafe { - mem::transmute::<&'this mut BorrowedBuf<'data>, &'this mut BorrowedBuf<'this>>(self) - }, - } - } - - /// Clears the buffer, resetting the filled region to empty. - /// - /// The number of initialized bytes is not changed, and the contents of the buffer are not modified. - #[inline] - pub fn clear(&mut self) -> &mut Self { - self.filled = 0; - self - } - - /// Asserts that the first `n` bytes of the buffer are initialized. - /// - /// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer - /// bytes than are already known to be initialized. - /// - /// # Safety - /// - /// The caller must ensure that the first `n` unfilled bytes of the buffer have already been initialized. - #[inline] - pub unsafe fn set_init(&mut self, n: usize) -> &mut Self { - self.init = cmp::max(self.init, n); - self - } -} - -/// A writeable view of the unfilled portion of a [`BorrowedBuf`](BorrowedBuf). -/// -/// Provides access to the initialized and uninitialized parts of the underlying `BorrowedBuf`. -/// Data can be written directly to the cursor by using [`append`](BorrowedCursor::append) or -/// indirectly by getting a slice of part or all of the cursor and writing into the slice. In the -/// indirect case, the caller must call [`advance`](BorrowedCursor::advance) after writing to inform -/// the cursor how many bytes have been written. -/// -/// Once data is written to the cursor, it becomes part of the filled portion of the underlying -/// `BorrowedBuf` and can no longer be accessed or re-written by the cursor. I.e., the cursor tracks -/// the unfilled part of the underlying `BorrowedBuf`. -/// -/// The lifetime `'a` is a bound on the lifetime of the underlying buffer (which means it is a bound -/// on the data in that buffer by transitivity). -#[derive(Debug)] -pub struct BorrowedCursor<'a> { - /// The underlying buffer. - // Safety invariant: we treat the type of buf as covariant in the lifetime of `BorrowedBuf` when - // we create a `BorrowedCursor`. This is only safe if we never replace `buf` by assigning into - // it, so don't do that! - buf: &'a mut BorrowedBuf<'a>, - /// The length of the filled portion of the underlying buffer at the time of the cursor's - /// creation. - start: usize, -} - -impl<'a> BorrowedCursor<'a> { - /// Reborrow this cursor by cloning it with a smaller lifetime. - /// - /// Since a cursor maintains unique access to its underlying buffer, the borrowed cursor is - /// not accessible while the new cursor exists. - #[inline] - pub fn reborrow<'this>(&'this mut self) -> BorrowedCursor<'this> { - BorrowedCursor { - // SAFETY: we never assign into `BorrowedCursor::buf`, so treating its - // lifetime covariantly is safe. - buf: unsafe { - mem::transmute::<&'this mut BorrowedBuf<'a>, &'this mut BorrowedBuf<'this>>( - self.buf, - ) - }, - start: self.start, - } - } - - /// Returns the available space in the cursor. - #[inline] - pub fn capacity(&self) -> usize { - self.buf.capacity() - self.buf.filled - } - - /// Returns the number of bytes written to this cursor since it was created from a `BorrowedBuf`. - /// - /// Note that if this cursor is a reborrowed clone of another, then the count returned is the - /// count written via either cursor, not the count since the cursor was reborrowed. - #[inline] - pub fn written(&self) -> usize { - self.buf.filled - self.start - } - - /// Returns a shared reference to the initialized portion of the cursor. - #[inline] - pub fn init_ref(&self) -> &[u8] { - // SAFETY: We only slice the initialized part of the buffer, which is always valid - unsafe { MaybeUninit::slice_assume_init_ref(&self.buf.buf[self.buf.filled..self.buf.init]) } - } - - /// Returns a mutable reference to the initialized portion of the cursor. - #[inline] - pub fn init_mut(&mut self) -> &mut [u8] { - // SAFETY: We only slice the initialized part of the buffer, which is always valid - unsafe { - MaybeUninit::slice_assume_init_mut(&mut self.buf.buf[self.buf.filled..self.buf.init]) - } - } - - /// Returns a mutable reference to the uninitialized part of the cursor. - /// - /// It is safe to uninitialize any of these bytes. - #[inline] - pub fn uninit_mut(&mut self) -> &mut [MaybeUninit<u8>] { - &mut self.buf.buf[self.buf.init..] - } - - /// Returns a mutable reference to the whole cursor. - /// - /// # Safety - /// - /// The caller must not uninitialize any bytes in the initialized portion of the cursor. - #[inline] - pub unsafe fn as_mut(&mut self) -> &mut [MaybeUninit<u8>] { - &mut self.buf.buf[self.buf.filled..] - } - - /// Advance the cursor by asserting that `n` bytes have been filled. - /// - /// After advancing, the `n` bytes are no longer accessible via the cursor and can only be - /// accessed via the underlying buffer. I.e., the buffer's filled portion grows by `n` elements - /// and its unfilled portion (and the capacity of this cursor) shrinks by `n` elements. - /// - /// # Safety - /// - /// The caller must ensure that the first `n` bytes of the cursor have been properly - /// initialised. - #[inline] - pub unsafe fn advance(&mut self, n: usize) -> &mut Self { - self.buf.filled += n; - self.buf.init = cmp::max(self.buf.init, self.buf.filled); - self - } - - /// Initializes all bytes in the cursor. - #[inline] - pub fn ensure_init(&mut self) -> &mut Self { - let uninit = self.uninit_mut(); - // SAFETY: 0 is a valid value for MaybeUninit<u8> and the length matches the allocation - // since it is comes from a slice reference. - unsafe { - ptr::write_bytes(uninit.as_mut_ptr(), 0, uninit.len()); - } - self.buf.init = self.buf.capacity(); - - self - } - - /// Asserts that the first `n` unfilled bytes of the cursor are initialized. - /// - /// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when - /// called with fewer bytes than are already known to be initialized. - /// - /// # Safety - /// - /// The caller must ensure that the first `n` bytes of the buffer have already been initialized. - #[inline] - pub unsafe fn set_init(&mut self, n: usize) -> &mut Self { - self.buf.init = cmp::max(self.buf.init, self.buf.filled + n); - self - } - - /// Appends data to the cursor, advancing position within its buffer. - /// - /// # Panics - /// - /// Panics if `self.capacity()` is less than `buf.len()`. - #[inline] - pub fn append(&mut self, buf: &[u8]) { - assert!(self.capacity() >= buf.len()); - - // SAFETY: we do not de-initialize any of the elements of the slice - unsafe { - MaybeUninit::write_slice(&mut self.as_mut()[..buf.len()], buf); - } - - // SAFETY: We just added the entire contents of buf to the filled section. - unsafe { - self.set_init(buf.len()); - } - self.buf.filled += buf.len(); - } -} - -impl<'a> Write for BorrowedCursor<'a> { - fn write(&mut self, buf: &[u8]) -> Result<usize> { - self.append(buf); - Ok(buf.len()) - } - - #[inline] - fn flush(&mut self) -> Result<()> { - Ok(()) - } -} diff --git a/library/std/src/io/readbuf/tests.rs b/library/std/src/io/readbuf/tests.rs deleted file mode 100644 index 89a2f6b22..000000000 --- a/library/std/src/io/readbuf/tests.rs +++ /dev/null @@ -1,175 +0,0 @@ -use super::BorrowedBuf; -use crate::mem::MaybeUninit; - -/// Test that BorrowedBuf has the correct numbers when created with new -#[test] -fn new() { - let buf: &mut [_] = &mut [0; 16]; - let mut rbuf: BorrowedBuf<'_> = buf.into(); - - assert_eq!(rbuf.filled().len(), 0); - assert_eq!(rbuf.init_len(), 16); - assert_eq!(rbuf.capacity(), 16); - assert_eq!(rbuf.unfilled().capacity(), 16); -} - -/// Test that BorrowedBuf has the correct numbers when created with uninit -#[test] -fn uninit() { - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16]; - let mut rbuf: BorrowedBuf<'_> = buf.into(); - - assert_eq!(rbuf.filled().len(), 0); - assert_eq!(rbuf.init_len(), 0); - assert_eq!(rbuf.capacity(), 16); - assert_eq!(rbuf.unfilled().capacity(), 16); -} - -#[test] -fn initialize_unfilled() { - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16]; - let mut rbuf: BorrowedBuf<'_> = buf.into(); - - rbuf.unfilled().ensure_init(); - - assert_eq!(rbuf.init_len(), 16); -} - -#[test] -fn advance_filled() { - let buf: &mut [_] = &mut [0; 16]; - let mut rbuf: BorrowedBuf<'_> = buf.into(); - - unsafe { - rbuf.unfilled().advance(1); - } - - assert_eq!(rbuf.filled().len(), 1); - assert_eq!(rbuf.unfilled().capacity(), 15); -} - -#[test] -fn clear() { - let buf: &mut [_] = &mut [255; 16]; - let mut rbuf: BorrowedBuf<'_> = buf.into(); - - unsafe { - rbuf.unfilled().advance(16); - } - - assert_eq!(rbuf.filled().len(), 16); - assert_eq!(rbuf.unfilled().capacity(), 0); - - rbuf.clear(); - - assert_eq!(rbuf.filled().len(), 0); - assert_eq!(rbuf.unfilled().capacity(), 16); - - assert_eq!(rbuf.unfilled().init_ref(), [255; 16]); -} - -#[test] -fn set_init() { - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16]; - let mut rbuf: BorrowedBuf<'_> = buf.into(); - - unsafe { - rbuf.set_init(8); - } - - assert_eq!(rbuf.init_len(), 8); - - unsafe { - rbuf.unfilled().advance(4); - } - - unsafe { - rbuf.set_init(2); - } - - assert_eq!(rbuf.init_len(), 8); - - unsafe { - rbuf.set_init(8); - } - - assert_eq!(rbuf.init_len(), 8); -} - -#[test] -fn append() { - let buf: &mut [_] = &mut [MaybeUninit::new(255); 16]; - let mut rbuf: BorrowedBuf<'_> = buf.into(); - - rbuf.unfilled().append(&[0; 8]); - - assert_eq!(rbuf.init_len(), 8); - assert_eq!(rbuf.filled().len(), 8); - assert_eq!(rbuf.filled(), [0; 8]); - - rbuf.clear(); - - rbuf.unfilled().append(&[1; 16]); - - assert_eq!(rbuf.init_len(), 16); - assert_eq!(rbuf.filled().len(), 16); - assert_eq!(rbuf.filled(), [1; 16]); -} - -#[test] -fn reborrow_written() { - let buf: &mut [_] = &mut [MaybeUninit::new(0); 32]; - let mut buf: BorrowedBuf<'_> = buf.into(); - - let mut cursor = buf.unfilled(); - cursor.append(&[1; 16]); - - let mut cursor2 = cursor.reborrow(); - cursor2.append(&[2; 16]); - - assert_eq!(cursor2.written(), 32); - assert_eq!(cursor.written(), 32); - - assert_eq!(buf.unfilled().written(), 0); - assert_eq!(buf.init_len(), 32); - assert_eq!(buf.filled().len(), 32); - let filled = buf.filled(); - assert_eq!(&filled[..16], [1; 16]); - assert_eq!(&filled[16..], [2; 16]); -} - -#[test] -fn cursor_set_init() { - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16]; - let mut rbuf: BorrowedBuf<'_> = buf.into(); - - unsafe { - rbuf.unfilled().set_init(8); - } - - assert_eq!(rbuf.init_len(), 8); - assert_eq!(rbuf.unfilled().init_ref().len(), 8); - assert_eq!(rbuf.unfilled().init_mut().len(), 8); - assert_eq!(rbuf.unfilled().uninit_mut().len(), 8); - assert_eq!(unsafe { rbuf.unfilled().as_mut() }.len(), 16); - - unsafe { - rbuf.unfilled().advance(4); - } - - unsafe { - rbuf.unfilled().set_init(2); - } - - assert_eq!(rbuf.init_len(), 8); - - unsafe { - rbuf.unfilled().set_init(8); - } - - assert_eq!(rbuf.init_len(), 12); - assert_eq!(rbuf.unfilled().init_ref().len(), 8); - assert_eq!(rbuf.unfilled().init_mut().len(), 8); - assert_eq!(rbuf.unfilled().uninit_mut().len(), 4); - assert_eq!(unsafe { rbuf.unfilled().as_mut() }.len(), 12); -} diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 9098d36ee..05b21eeb4 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -611,6 +611,7 @@ static STDOUT: OnceLock<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = OnceLo /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "io_stdout")] pub fn stdout() -> Stdout { Stdout { inner: STDOUT @@ -847,6 +848,7 @@ pub struct StderrLock<'a> { /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "io_stderr")] pub fn stderr() -> Stderr { // Note that unlike `stdout()` we don't use `at_exit` here to register a // destructor. Stderr is not buffered, so there's no need to run a diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f1f0f8b16..425890122 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -227,6 +227,7 @@ test(no_crate_inject, attr(deny(warnings))), test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] +#![cfg_attr(not(bootstrap), doc(rust_logo))] #![doc(cfg_hide( not(test), not(any(test, bootstrap)), @@ -259,7 +260,7 @@ all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) )] -#![cfg_attr(windows, feature(round_char_boundary))] +#![cfg_attr(any(windows, target_os = "uefi"), feature(round_char_boundary))] #![cfg_attr(target_os = "xous", feature(slice_ptr_len))] // // Language features: @@ -270,6 +271,7 @@ #![feature(allow_internal_unstable)] #![feature(c_unwind)] #![feature(cfg_target_thread_local)] +#![feature(cfi_encoding)] #![feature(concat_idents)] #![feature(const_mut_refs)] #![feature(const_trait_impl)] @@ -292,6 +294,7 @@ #![feature(needs_panic_runtime)] #![feature(negative_impls)] #![feature(never_type)] +#![feature(no_sanitize)] #![feature(platform_intrinsics)] #![feature(prelude_import)] #![feature(rustc_attrs)] @@ -307,6 +310,7 @@ // tidy-alphabetical-start #![feature(char_internals)] #![feature(core_intrinsics)] +#![feature(core_io_borrowed_buf)] #![feature(duration_constants)] #![feature(error_generic_member_access)] #![feature(error_in_core)] @@ -328,7 +332,6 @@ #![feature(panic_can_unwind)] #![feature(panic_info_message)] #![feature(panic_internals)] -#![feature(pointer_byte_offsets)] #![feature(pointer_is_aligned)] #![feature(portable_simd)] #![feature(prelude_2024)] diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 227e418b7..60347a11d 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -99,6 +99,16 @@ impl UdpSocket { /// /// let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); /// ``` + /// + /// Note that `bind` declares the scope of your network connection. + /// You can only receive datagrams from and send datagrams to + /// participants in that view of the network. + /// For instance, binding to a loopback address as in the example + /// above will prevent you from sending datagrams to another device + /// in your local network. + /// + /// In order to limit your view of the network the least, `bind` to + /// [`Ipv4Addr::UNSPECIFIED`] or [`Ipv6Addr::UNSPECIFIED`]. #[stable(feature = "rust1", since = "1.0.0")] pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> { super::each_addr(addr, net_imp::UdpSocket::bind).map(UdpSocket) @@ -157,7 +167,9 @@ impl UdpSocket { } /// Sends data on the socket to the given address. On success, returns the - /// number of bytes written. + /// number of bytes written. Note that the operating system may refuse + /// buffers larger than 65507. However, partial writes are not possible + /// until buffer sizes above `i32::MAX`. /// /// Address type can be any implementor of [`ToSocketAddrs`] trait. See its /// documentation for concrete examples. @@ -652,12 +664,19 @@ impl UdpSocket { /// function of a UDP socket is not a useful thing to do: The OS will be /// unable to determine whether something is listening on the remote /// address without the application sending data. + /// + /// If your first `connect` is to a loopback address, subsequent + /// `connect`s to non-loopback addresses might fail, depending + /// on the platform. #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> io::Result<()> { super::each_addr(addr, |addr| self.0.connect(addr)) } /// Sends data on the socket to the remote address to which it is connected. + /// On success, returns the number of bytes written. Note that the operating + /// system may refuse buffers larger than 65507. However, partial writes are + /// not possible until buffer sizes above `i32::MAX`. /// /// [`UdpSocket::connect`] will connect this socket to a remote address. This /// method will fail if the socket is not connected. diff --git a/library/std/src/os/aix/fs.rs b/library/std/src/os/aix/fs.rs new file mode 100644 index 000000000..ac9dd45f0 --- /dev/null +++ b/library/std/src/os/aix/fs.rs @@ -0,0 +1,348 @@ +//! AIX specific extensions to primitives in the [`std::fs`] module. +//! +//! [`std::fs`]: crate::fs + +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Returns the device ID on which this file resides. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_dev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + /// Returns the inode number. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ino()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + /// Returns the file type and mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mode()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + /// Returns the number of hard links to file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_nlink()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + /// Returns the user ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_uid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + /// Returns the group ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_gid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + /// Returns the device ID that this file represents. Only relevant for special file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_rdev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes. + /// + /// The size of a symbolic link is the length of the pathname it contains, + /// without a terminating null byte. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_size()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + /// Returns the last access time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + /// Returns the last access time of the file, in nanoseconds since [`st_atime`]. + /// + /// [`st_atime`]: Self::st_atime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + /// Returns the last modification time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + /// Returns the last modification time of the file, in nanoseconds since [`st_mtime`]. + /// + /// [`st_mtime`]: Self::st_mtime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + /// Returns the last status change time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + /// Returns the last status change time of the file, in nanoseconds since [`st_ctime`]. + /// + /// [`st_ctime`]: Self::st_ctime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + /// Returns the "preferred" block size for efficient filesystem I/O. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blksize()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + /// Returns the number of blocks allocated to the file, 512-byte units. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::aix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blocks()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime.tv_sec as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime.tv_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime.tv_sec as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime.tv_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime.tv_sec as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime.tv_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/library/std/src/os/aix/mod.rs b/library/std/src/os/aix/mod.rs new file mode 100644 index 000000000..7f86a3c77 --- /dev/null +++ b/library/std/src/os/aix/mod.rs @@ -0,0 +1,6 @@ +//! AIX specific definitions. + +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod fs; +pub mod raw; diff --git a/library/std/src/os/aix/raw.rs b/library/std/src/os/aix/raw.rs new file mode 100644 index 000000000..b4c8dc72c --- /dev/null +++ b/library/std/src/os/aix/raw.rs @@ -0,0 +1,9 @@ +//! AIX specific raw type definitions. + +#![stable(feature = "raw_ext", since = "1.1.0")] + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub use libc::pthread_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use libc::{blkcnt_t, blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t, stat, time_t}; diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 81106d6c6..24f2bdcf4 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -97,14 +97,14 @@ impl BorrowedFd<'_> { // We want to atomically duplicate this file descriptor and set the // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This // is a POSIX flag that was added to Linux in 2.6.24. - #[cfg(not(target_os = "espidf"))] + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] let cmd = libc::F_DUPFD_CLOEXEC; // For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics // will never be supported, as this is a bare metal framework with // no capabilities for multi-process execution. While F_DUPFD is also // not supported yet, it might be (currently it returns ENOSYS). - #[cfg(target_os = "espidf")] + #[cfg(any(target_os = "espidf", target_os = "vita"))] let cmd = libc::F_DUPFD; // Avoid using file descriptors below 3 as they are used for stdio @@ -119,7 +119,7 @@ impl BorrowedFd<'_> { pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> { Err(crate::io::const_io_error!( crate::io::ErrorKind::Unsupported, - "operation not supported on WASI yet", + "operation not supported on this platform", )) } } diff --git a/library/std/src/os/freebsd/fs.rs b/library/std/src/os/freebsd/fs.rs index 8db3a950c..5689a82e0 100644 --- a/library/std/src/os/freebsd/fs.rs +++ b/library/std/src/os/freebsd/fs.rs @@ -76,12 +76,7 @@ impl MetadataExt for Metadata { fn as_raw_stat(&self) -> &raw::stat { // The methods below use libc::stat, so they work fine when libc is built with FreeBSD 12 ABI. // This method would just return nonsense. - #[cfg(freebsd12)] panic!("as_raw_stat not supported with FreeBSD 12 ABI"); - #[cfg(not(freebsd12))] - unsafe { - &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) - } } fn st_dev(&self) -> u64 { self.as_inner().as_inner().st_dev as u64 @@ -143,12 +138,7 @@ impl MetadataExt for Metadata { fn st_flags(&self) -> u32 { self.as_inner().as_inner().st_flags as u32 } - #[cfg(freebsd12)] fn st_lspare(&self) -> u32 { panic!("st_lspare not supported with FreeBSD 12 ABI"); } - #[cfg(not(freebsd12))] - fn st_lspare(&self) -> u32 { - self.as_inner().as_inner().st_lspare as u32 - } } diff --git a/library/std/src/os/ios/fs.rs b/library/std/src/os/ios/fs.rs index b319527a5..e5df4de0b 100644 --- a/library/std/src/os/ios/fs.rs +++ b/library/std/src/os/ios/fs.rs @@ -144,14 +144,14 @@ impl MetadataExt for Metadata { } /// OS-specific extensions to [`fs::FileTimes`]. -#[unstable(feature = "file_set_times", issue = "98245")] +#[stable(feature = "file_set_times", since = "1.75.0")] pub trait FileTimesExt: Sealed { /// Set the creation time of a file. - #[unstable(feature = "file_set_times", issue = "98245")] + #[stable(feature = "file_set_times", since = "1.75.0")] fn set_created(self, t: SystemTime) -> Self; } -#[unstable(feature = "file_set_times", issue = "98245")] +#[stable(feature = "file_set_times", since = "1.75.0")] impl FileTimesExt for fs::FileTimes { fn set_created(mut self, t: SystemTime) -> Self { self.as_inner_mut().set_created(t.into_inner()); diff --git a/library/std/src/os/linux/fs.rs b/library/std/src/os/linux/fs.rs index 479bbcc17..ab0b2a3ed 100644 --- a/library/std/src/os/linux/fs.rs +++ b/library/std/src/os/linux/fs.rs @@ -329,7 +329,14 @@ pub trait MetadataExt { impl MetadataExt for Metadata { #[allow(deprecated)] fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat) } + #[cfg(target_env = "musl")] + unsafe { + &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) + } + #[cfg(not(target_env = "musl"))] + unsafe { + &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat) + } } fn st_dev(&self) -> u64 { self.as_inner().as_inner().st_dev as u64 diff --git a/library/std/src/os/macos/fs.rs b/library/std/src/os/macos/fs.rs index fe82d03d8..573426d1a 100644 --- a/library/std/src/os/macos/fs.rs +++ b/library/std/src/os/macos/fs.rs @@ -150,14 +150,14 @@ impl MetadataExt for Metadata { } /// OS-specific extensions to [`fs::FileTimes`]. -#[unstable(feature = "file_set_times", issue = "98245")] +#[stable(feature = "file_set_times", since = "1.75.0")] pub trait FileTimesExt: Sealed { /// Set the creation time of a file. - #[unstable(feature = "file_set_times", issue = "98245")] + #[stable(feature = "file_set_times", since = "1.75.0")] fn set_created(self, t: SystemTime) -> Self; } -#[unstable(feature = "file_set_times", issue = "98245")] +#[stable(feature = "file_set_times", since = "1.75.0")] impl FileTimesExt for fs::FileTimes { fn set_created(mut self, t: SystemTime) -> Self { self.as_inner_mut().set_created(t.into_inner()); diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 11ad21515..6e11b92b6 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -97,6 +97,8 @@ pub mod wasi; pub mod windows; // Others. +#[cfg(target_os = "aix")] +pub mod aix; #[cfg(target_os = "android")] pub mod android; #[cfg(target_os = "dragonfly")] diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 3724e90af..5ba8719e6 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -37,6 +37,8 @@ use crate::os::linux as platform; #[cfg(not(doc))] mod platform { + #[cfg(target_os = "aix")] + pub use crate::os::aix::*; #[cfg(target_os = "android")] pub use crate::os::android::*; #[cfg(target_os = "dragonfly")] diff --git a/library/std/src/os/watchos/fs.rs b/library/std/src/os/watchos/fs.rs index 2ecc4c68a..ee215dd59 100644 --- a/library/std/src/os/watchos/fs.rs +++ b/library/std/src/os/watchos/fs.rs @@ -144,14 +144,14 @@ impl MetadataExt for Metadata { } /// OS-specific extensions to [`fs::FileTimes`]. -#[unstable(feature = "file_set_times", issue = "98245")] +#[stable(feature = "file_set_times", since = "1.75.0")] pub trait FileTimesExt: Sealed { /// Set the creation time of a file. - #[unstable(feature = "file_set_times", issue = "98245")] + #[stable(feature = "file_set_times", since = "1.75.0")] fn set_created(self, t: SystemTime) -> Self; } -#[unstable(feature = "file_set_times", issue = "98245")] +#[stable(feature = "file_set_times", since = "1.75.0")] impl FileTimesExt for fs::FileTimes { fn set_created(mut self, t: SystemTime) -> Self { self.as_inner_mut().set_created(t.into_inner()); diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 94509e547..1b013d1c1 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -528,14 +528,14 @@ impl FileTypeExt for fs::FileType { } /// Windows-specific extensions to [`fs::FileTimes`]. -#[unstable(feature = "file_set_times", issue = "98245")] +#[stable(feature = "file_set_times", since = "1.75.0")] pub trait FileTimesExt: Sealed { /// Set the creation time of a file. - #[unstable(feature = "file_set_times", issue = "98245")] + #[stable(feature = "file_set_times", since = "1.75.0")] fn set_created(self, t: SystemTime) -> Self; } -#[unstable(feature = "file_set_times", issue = "98245")] +#[stable(feature = "file_set_times", since = "1.75.0")] impl FileTimesExt for fs::FileTimes { fn set_created(mut self, t: SystemTime) -> Self { self.as_inner_mut().set_created(t.into_inner()); diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index d7a2baa1f..55f4917a9 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -295,12 +295,53 @@ fn default_hook(info: &PanicInfo<'_>) { #[cfg(not(test))] #[doc(hidden)] +#[cfg(feature = "panic_immediate_abort")] +#[unstable(feature = "update_panic_count", issue = "none")] +pub mod panic_count { + /// A reason for forcing an immediate abort on panic. + #[derive(Debug)] + pub enum MustAbort { + AlwaysAbort, + PanicInHook, + } + + #[inline] + pub fn increase(run_panic_hook: bool) -> Option<MustAbort> { + None + } + + #[inline] + pub fn finished_panic_hook() {} + + #[inline] + pub fn decrease() {} + + #[inline] + pub fn set_always_abort() {} + + // Disregards ALWAYS_ABORT_FLAG + #[inline] + #[must_use] + pub fn get_count() -> usize { + 0 + } + + #[must_use] + #[inline] + pub fn count_is_zero() -> bool { + true + } +} + +#[cfg(not(test))] +#[doc(hidden)] +#[cfg(not(feature = "panic_immediate_abort"))] #[unstable(feature = "update_panic_count", issue = "none")] pub mod panic_count { use crate::cell::Cell; use crate::sync::atomic::{AtomicUsize, Ordering}; - pub const ALWAYS_ABORT_FLAG: usize = 1 << (usize::BITS - 1); + const ALWAYS_ABORT_FLAG: usize = 1 << (usize::BITS - 1); /// A reason for forcing an immediate abort on panic. #[derive(Debug)] @@ -421,6 +462,13 @@ pub mod panic_count { pub use realstd::rt::panic_count; /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. +#[cfg(feature = "panic_immediate_abort")] +pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> { + Ok(f()) +} + +/// Invoke a closure, capturing the cause of an unwinding panic if one occurs. +#[cfg(not(feature = "panic_immediate_abort"))] pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> { union Data<F, R> { f: ManuallyDrop<F>, @@ -755,6 +803,7 @@ fn rust_panic_with_hook( /// This is the entry point for `resume_unwind`. /// It just forwards the payload to the panic runtime. +#[cfg_attr(feature = "panic_immediate_abort", inline)] pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! { panic_count::increase(false); @@ -777,7 +826,16 @@ pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! { /// yer breakpoints. #[inline(never)] #[cfg_attr(not(test), rustc_std_internal_symbol)] +#[cfg(not(feature = "panic_immediate_abort"))] fn rust_panic(msg: &mut dyn PanicPayload) -> ! { let code = unsafe { __rust_start_panic(msg) }; rtabort!("failed to initiate panic, error {code}") } + +#[cfg_attr(not(test), rustc_std_internal_symbol)] +#[cfg(feature = "panic_immediate_abort")] +fn rust_panic(_: &mut dyn PanicPayload) -> ! { + unsafe { + crate::intrinsics::abort(); + } +} diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 8c1497613..af6bef1a7 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -526,6 +526,7 @@ impl fmt::Debug for ChildStderr { /// list_dir.status().expect("process failed to execute"); /// ``` #[stable(feature = "process", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Command")] pub struct Command { inner: imp::Command, } @@ -607,7 +608,7 @@ impl Command { /// /// Note that the argument is not passed through a shell, but given /// literally to the program. This means that shell syntax like quotes, - /// escaped characters, word splitting, glob patterns, substitution, etc. + /// escaped characters, word splitting, glob patterns, variable substitution, etc. /// have no effect. /// /// # Examples @@ -637,7 +638,7 @@ impl Command { /// /// Note that the arguments are not passed through a shell, but given /// literally to the program. This means that shell syntax like quotes, - /// escaped characters, word splitting, glob patterns, substitution, etc. + /// escaped characters, word splitting, glob patterns, variable substitution, etc. /// have no effect. /// /// # Examples @@ -1593,7 +1594,7 @@ impl From<io::Stderr> for Stdio { pub struct ExitStatus(imp::ExitStatus); /// The default value is one which indicates successful completion. -#[stable(feature = "process-exitcode-default", since = "1.73.0")] +#[stable(feature = "process_exitstatus_default", since = "1.73.0")] impl Default for ExitStatus { fn default() -> Self { // Ideally this would be done by ExitCode::default().into() but that is complicated. @@ -1959,6 +1960,14 @@ impl ExitCode { } } +/// The default value is [`ExitCode::SUCCESS`] +#[stable(feature = "process_exitcode_default", since = "1.75.0")] +impl Default for ExitCode { + fn default() -> Self { + ExitCode::SUCCESS + } +} + #[stable(feature = "process_exitcode", since = "1.61.0")] impl From<u8> for ExitCode { /// Construct an `ExitCode` from an arbitrary u8 value. @@ -2196,6 +2205,7 @@ impl Child { /// process::exit(0x0100); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "process_exit")] pub fn exit(code: i32) -> ! { crate::rt::cleanup(); crate::sys::os::exit(code) diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index f1eeb75be..5c83f72f3 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -155,6 +155,7 @@ fn lang_start_internal( } #[cfg(not(test))] +#[inline(never)] #[lang = "start"] fn lang_start<T: crate::process::Termination + 'static>( main: fn() -> T, diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 8c46080e4..2bb4f3f9e 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -125,7 +125,7 @@ impl Once { /// /// # Panics /// - /// The closure `f` will only be executed once if this is called + /// The closure `f` will only be executed once even if this is called /// concurrently amongst many threads. If that closure panics, however, then /// it will *poison* this [`Once`] instance, causing all future invocations of /// `call_once` to also panic. diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index e2b7b893c..f49630907 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -126,11 +126,48 @@ impl<T> OnceLock<T> { #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn set(&self, value: T) -> Result<(), T> { + match self.try_insert(value) { + Ok(_) => Ok(()), + Err((_, value)) => Err(value), + } + } + + /// Sets the contents of this cell to `value` if the cell was empty, then + /// returns a reference to it. + /// + /// May block if another thread is currently attempting to initialize the cell. The cell is + /// guaranteed to contain a value when set returns, though not necessarily the one provided. + /// + /// Returns `Ok(&value)` if the cell was empty and `Err(¤t_value, value)` if it was full. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell_try_insert)] + /// + /// use std::sync::OnceLock; + /// + /// static CELL: OnceLock<i32> = OnceLock::new(); + /// + /// fn main() { + /// assert!(CELL.get().is_none()); + /// + /// std::thread::spawn(|| { + /// assert_eq!(CELL.try_insert(92), Ok(&92)); + /// }).join().unwrap(); + /// + /// assert_eq!(CELL.try_insert(62), Err((&92, 62))); + /// assert_eq!(CELL.get(), Some(&92)); + /// } + /// ``` + #[inline] + #[unstable(feature = "once_cell_try_insert", issue = "116693")] + pub fn try_insert(&self, value: T) -> Result<&T, (&T, T)> { let mut value = Some(value); - self.get_or_init(|| value.take().unwrap()); + let res = self.get_or_init(|| value.take().unwrap()); match value { - None => Ok(()), - Some(value) => Err(value), + None => Ok(res), + Some(value) => Err((res, value)), } } diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index 26aaa2414..ac7c800ff 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -380,7 +380,7 @@ impl<T: ?Sized> RwLock<T> { /// /// If the lock is poisoned, it will remain poisoned until this function is called. This allows /// recovering from a poisoned state and marking that it has recovered. For example, if the - /// value is overwritten by a known-good value, then the mutex can be marked as un-poisoned. Or + /// value is overwritten by a known-good value, then the lock can be marked as un-poisoned. Or /// possibly, the value could be inspected to determine if it is in a consistent state, and if /// so the poison is removed. /// @@ -397,7 +397,7 @@ impl<T: ?Sized> RwLock<T> { /// /// let _ = thread::spawn(move || { /// let _lock = c_lock.write().unwrap(); - /// panic!(); // the mutex gets poisoned + /// panic!(); // the lock gets poisoned /// }).join(); /// /// assert_eq!(lock.is_poisoned(), true); diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs index 2b8782ddf..b35c5d30b 100644 --- a/library/std/src/sys/common/mod.rs +++ b/library/std/src/sys/common/mod.rs @@ -12,6 +12,7 @@ pub mod alloc; pub mod small_c_string; +#[allow(unused_imports)] pub mod thread_local; #[cfg(test)] diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs index a564f1698..bd8b493d6 100644 --- a/library/std/src/sys/hermit/net.rs +++ b/library/std/src/sys/hermit/net.rs @@ -56,6 +56,12 @@ impl Socket { unimplemented!() } + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; + Ok(()) + } + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { self.set_nonblocking(true)?; let r = unsafe { diff --git a/library/std/src/sys/hermit/thread_local_dtor.rs b/library/std/src/sys/hermit/thread_local_dtor.rs index 613266b95..98adaf4bf 100644 --- a/library/std/src/sys/hermit/thread_local_dtor.rs +++ b/library/std/src/sys/hermit/thread_local_dtor.rs @@ -5,23 +5,25 @@ // The this solution works like the implementation of macOS and // doesn't additional OS support -use crate::mem; +use crate::cell::RefCell; #[thread_local] -static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); +static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new()); pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - let list = &mut DTORS; - list.push((t, dtor)); + match DTORS.try_borrow_mut() { + Ok(mut dtors) => dtors.push((t, dtor)), + Err(_) => rtabort!("global allocator may not use TLS"), + } } // every thread call this function to run through all possible destructors pub unsafe fn run_dtors() { - let mut list = mem::take(&mut DTORS); + let mut list = DTORS.take(); while !list.is_empty() { for (ptr, dtor) in list { dtor(ptr); } - list = mem::take(&mut DTORS); + list = DTORS.take(); } } diff --git a/library/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs index 79624703a..a78084de0 100644 --- a/library/std/src/sys/personality/dwarf/eh.rs +++ b/library/std/src/sys/personality/dwarf/eh.rs @@ -1,6 +1,7 @@ //! Parsing of GCC-style Language-Specific Data Area (LSDA) //! For details see: //! * <https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html> +//! * <https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html> //! * <https://itanium-cxx-abi.github.io/cxx-abi/exceptions.pdf> //! * <https://www.airs.com/blog/archives/460> //! * <https://www.airs.com/blog/archives/464> @@ -37,17 +38,19 @@ pub const DW_EH_PE_indirect: u8 = 0x80; #[derive(Copy, Clone)] pub struct EHContext<'a> { - pub ip: usize, // Current instruction pointer - pub func_start: usize, // Address of the current function - pub get_text_start: &'a dyn Fn() -> usize, // Get address of the code section - pub get_data_start: &'a dyn Fn() -> usize, // Get address of the data section + pub ip: *const u8, // Current instruction pointer + pub func_start: *const u8, // Pointer to the current function + pub get_text_start: &'a dyn Fn() -> *const u8, // Get pointer to the code section + pub get_data_start: &'a dyn Fn() -> *const u8, // Get pointer to the data section } +/// Landing pad. +type LPad = *const u8; pub enum EHAction { None, - Cleanup(usize), - Catch(usize), - Filter(usize), + Cleanup(LPad), + Catch(LPad), + Filter(LPad), Terminate, } @@ -81,22 +84,24 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result let ip = context.ip; if !USING_SJLJ_EXCEPTIONS { + // read the callsite table while reader.ptr < action_table { - let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding)?; - let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding)?; - let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding)?; + // these are offsets rather than pointers; + let cs_start = read_encoded_offset(&mut reader, call_site_encoding)?; + let cs_len = read_encoded_offset(&mut reader, call_site_encoding)?; + let cs_lpad = read_encoded_offset(&mut reader, call_site_encoding)?; let cs_action_entry = reader.read_uleb128(); // Callsite table is sorted by cs_start, so if we've passed the ip, we // may stop searching. - if ip < func_start + cs_start { + if ip < func_start.wrapping_add(cs_start) { break; } - if ip < func_start + cs_start + cs_len { + if ip < func_start.wrapping_add(cs_start + cs_len) { if cs_lpad == 0 { return Ok(EHAction::None); } else { - let lpad = lpad_base + cs_lpad; - return Ok(interpret_cs_action(action_table as *mut u8, cs_action_entry, lpad)); + let lpad = lpad_base.wrapping_add(cs_lpad); + return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); } } } @@ -106,12 +111,12 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result // SjLj version: // The "IP" is an index into the call-site table, with two exceptions: // -1 means 'no-action', and 0 means 'terminate'. - match ip as isize { + match ip.addr() as isize { -1 => return Ok(EHAction::None), 0 => return Ok(EHAction::Terminate), _ => (), } - let mut idx = ip; + let mut idx = ip.addr(); loop { let cs_lpad = reader.read_uleb128(); let cs_action_entry = reader.read_uleb128(); @@ -119,17 +124,18 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result if idx == 0 { // Can never have null landing pad for sjlj -- that would have // been indicated by a -1 call site index. - let lpad = (cs_lpad + 1) as usize; - return Ok(interpret_cs_action(action_table as *mut u8, cs_action_entry, lpad)); + // FIXME(strict provenance) + let lpad = ptr::from_exposed_addr((cs_lpad + 1) as usize); + return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); } } } } unsafe fn interpret_cs_action( - action_table: *mut u8, + action_table: *const u8, cs_action_entry: u64, - lpad: usize, + lpad: LPad, ) -> EHAction { if cs_action_entry == 0 { // If cs_action_entry is 0 then this is a cleanup (Drop::drop). We run these @@ -138,7 +144,7 @@ unsafe fn interpret_cs_action( } else { // If lpad != 0 and cs_action_entry != 0, we have to check ttype_index. // If ttype_index == 0 under the condition, we take cleanup action. - let action_record = (action_table as *mut u8).offset(cs_action_entry as isize - 1); + let action_record = action_table.offset(cs_action_entry as isize - 1); let mut action_reader = DwarfReader::new(action_record); let ttype_index = action_reader.read_sleb128(); if ttype_index == 0 { @@ -157,22 +163,24 @@ fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> { if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) } } -unsafe fn read_encoded_pointer( - reader: &mut DwarfReader, - context: &EHContext<'_>, - encoding: u8, -) -> Result<usize, ()> { - if encoding == DW_EH_PE_omit { +/// Read a offset (`usize`) from `reader` whose encoding is described by `encoding`. +/// +/// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext]. +/// In addition the upper ("application") part must be zero. +/// +/// # Errors +/// Returns `Err` if `encoding` +/// * is not a valid DWARF Exception Header Encoding, +/// * is `DW_EH_PE_omit`, or +/// * has a non-zero application part. +/// +/// [LSB-dwarf-ext]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html +unsafe fn read_encoded_offset(reader: &mut DwarfReader, encoding: u8) -> Result<usize, ()> { + if encoding == DW_EH_PE_omit || encoding & 0xF0 != 0 { return Err(()); } - - // DW_EH_PE_aligned implies it's an absolute pointer value - if encoding == DW_EH_PE_aligned { - reader.ptr = reader.ptr.with_addr(round_up(reader.ptr.addr(), mem::size_of::<usize>())?); - return Ok(reader.read::<usize>()); - } - - let mut result = match encoding & 0x0F { + let result = match encoding & 0x0F { + // despite the name, LLVM also uses absptr for offsets instead of pointers DW_EH_PE_absptr => reader.read::<usize>(), DW_EH_PE_uleb128 => reader.read_uleb128() as usize, DW_EH_PE_udata2 => reader.read::<u16>() as usize, @@ -184,25 +192,66 @@ unsafe fn read_encoded_pointer( DW_EH_PE_sdata8 => reader.read::<i64>() as usize, _ => return Err(()), }; + Ok(result) +} + +/// Read a pointer from `reader` whose encoding is described by `encoding`. +/// +/// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext]. +/// +/// # Errors +/// Returns `Err` if `encoding` +/// * is not a valid DWARF Exception Header Encoding, +/// * is `DW_EH_PE_omit`, or +/// * combines `DW_EH_PE_absptr` or `DW_EH_PE_aligned` application part with an integer encoding +/// (not `DW_EH_PE_absptr`) in the value format part. +/// +/// [LSB-dwarf-ext]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html +unsafe fn read_encoded_pointer( + reader: &mut DwarfReader, + context: &EHContext<'_>, + encoding: u8, +) -> Result<*const u8, ()> { + if encoding == DW_EH_PE_omit { + return Err(()); + } - result += match encoding & 0x70 { - DW_EH_PE_absptr => 0, + let base_ptr = match encoding & 0x70 { + DW_EH_PE_absptr => core::ptr::null(), // relative to address of the encoded value, despite the name - DW_EH_PE_pcrel => reader.ptr.expose_addr(), + DW_EH_PE_pcrel => reader.ptr, DW_EH_PE_funcrel => { - if context.func_start == 0 { + if context.func_start.is_null() { return Err(()); } context.func_start } DW_EH_PE_textrel => (*context.get_text_start)(), DW_EH_PE_datarel => (*context.get_data_start)(), + // aligned means the value is aligned to the size of a pointer + DW_EH_PE_aligned => { + reader.ptr = + reader.ptr.with_addr(round_up(reader.ptr.addr(), mem::size_of::<*const u8>())?); + core::ptr::null() + } _ => return Err(()), }; + let mut ptr = if base_ptr.is_null() { + // any value encoding other than absptr would be nonsensical here; + // there would be no source of pointer provenance + if encoding & 0x0F != DW_EH_PE_absptr { + return Err(()); + } + reader.read::<*const u8>() + } else { + let offset = read_encoded_offset(reader, encoding & 0x0F)?; + base_ptr.wrapping_add(offset) + }; + if encoding & DW_EH_PE_indirect != 0 { - result = *ptr::from_exposed_addr::<usize>(result); + ptr = *(ptr.cast::<*const u8>()); } - Ok(result) + Ok(ptr) } diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index e477a0cd7..6f3171311 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -38,7 +38,6 @@ use super::dwarf::eh::{self, EHAction, EHContext}; use crate::ffi::c_int; -use libc::uintptr_t; use unwind as uw; // Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister() @@ -95,7 +94,7 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 cfg_if::cfg_if! { if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "netbsd")))] { // ARM EHABI personality routine. - // https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf + // https://web.archive.org/web/20190728160938/https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf // // iOS uses the default routine instead since it uses SjLj unwinding. #[lang = "eh_personality"] @@ -160,9 +159,9 @@ cfg_if::cfg_if! { uw::_Unwind_SetGR( context, UNWIND_DATA_REG.0, - exception_object as uintptr_t, + exception_object as uw::_Unwind_Ptr, ); - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); uw::_Unwind_SetIP(context, lpad); return uw::_URC_INSTALL_CONTEXT; } @@ -222,9 +221,9 @@ cfg_if::cfg_if! { uw::_Unwind_SetGR( context, UNWIND_DATA_REG.0, - exception_object as uintptr_t, + exception_object.cast(), ); - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); uw::_Unwind_SetIP(context, lpad); uw::_URC_INSTALL_CONTEXT } diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs index 01505e944..817c33b66 100644 --- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs @@ -3,6 +3,8 @@ use crate::arch::asm; use crate::cell::UnsafeCell; use crate::cmp; +use crate::convert::TryInto; +use crate::intrinsics; use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut}; use crate::ptr::{self, NonNull}; @@ -306,20 +308,35 @@ where } } -// Split a memory region ptr..ptr + len into three parts: -// +--------+ -// | small0 | Chunk smaller than 8 bytes -// +--------+ -// | big | Chunk 8-byte aligned, and size a multiple of 8 bytes -// +--------+ -// | small1 | Chunk smaller than 8 bytes -// +--------+ -fn region_as_aligned_chunks(ptr: *const u8, len: usize) -> (usize, usize, usize) { - let small0_size = if ptr.is_aligned_to(8) { 0 } else { 8 - ptr.addr() % 8 }; - let small1_size = (len - small0_size) % 8; - let big_size = len - small0_size - small1_size; - - (small0_size, big_size, small1_size) +/// Divide the slice `(ptr, len)` into three parts, where the middle part is +/// aligned to `u64`. +/// +/// The return values `(prefix_len, mid_len, suffix_len)` add back up to `len`. +/// The return values are such that the memory region `(ptr + prefix_len, +/// mid_len)` is the largest possible region where `ptr + prefix_len` is aligned +/// to `u64` and `mid_len` is a multiple of the byte size of `u64`. This means +/// that `prefix_len` and `suffix_len` are guaranteed to be less than the byte +/// size of `u64`, and that `(ptr, prefix_len)` and `(ptr + prefix_len + +/// mid_len, suffix_len)` don't straddle an alignment boundary. +// Standard Rust functions such as `<[u8]>::align_to::<u64>` and +// `<*const u8>::align_offset` aren't _guaranteed_ to compute the largest +// possible middle region, and as such can't be used. +fn u64_align_to_guaranteed(ptr: *const u8, mut len: usize) -> (usize, usize, usize) { + const QWORD_SIZE: usize = mem::size_of::<u64>(); + + let offset = ptr as usize % QWORD_SIZE; + + let prefix_len = if intrinsics::unlikely(offset > 0) { QWORD_SIZE - offset } else { 0 }; + + len = match len.checked_sub(prefix_len) { + Some(remaining_len) => remaining_len, + None => return (len, 0, 0), + }; + + let suffix_len = len % QWORD_SIZE; + len -= suffix_len; + + (prefix_len, len, suffix_len) } unsafe fn copy_quadwords(src: *const u8, dst: *mut u8, len: usize) { @@ -352,7 +369,13 @@ unsafe fn copy_quadwords(src: *const u8, dst: *mut u8, len: usize) { /// - https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00615.html /// - https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/processor-mmio-stale-data-vulnerabilities.html#inpage-nav-3-2-2 pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize) { - unsafe fn copy_bytewise_to_userspace(src: *const u8, dst: *mut u8, len: usize) { + /// Like `ptr::copy(src, dst, len)`, except it uses the Intel-recommended + /// instruction sequence for unaligned writes. + unsafe fn write_bytewise_to_userspace(src: *const u8, dst: *mut u8, len: usize) { + if intrinsics::likely(len == 0) { + return; + } + unsafe { let mut seg_sel: u16 = 0; for off in 0..len { @@ -380,41 +403,15 @@ pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize) assert!(!src.addr().overflowing_add(len).1); assert!(!dst.addr().overflowing_add(len).1); - if len < 8 { - // Can't align on 8 byte boundary: copy safely byte per byte - unsafe { - copy_bytewise_to_userspace(src, dst, len); - } - } else if len % 8 == 0 && dst.is_aligned_to(8) { - // Copying 8-byte aligned quadwords: copy quad word per quad word - unsafe { - copy_quadwords(src, dst, len); - } - } else { - // Split copies into three parts: - // +--------+ - // | small0 | Chunk smaller than 8 bytes - // +--------+ - // | big | Chunk 8-byte aligned, and size a multiple of 8 bytes - // +--------+ - // | small1 | Chunk smaller than 8 bytes - // +--------+ - let (small0_size, big_size, small1_size) = region_as_aligned_chunks(dst, len); + unsafe { + let (len1, len2, len3) = u64_align_to_guaranteed(dst, len); + let (src1, dst1) = (src, dst); + let (src2, dst2) = (src1.add(len1), dst1.add(len1)); + let (src3, dst3) = (src2.add(len2), dst2.add(len2)); - unsafe { - // Copy small0 - copy_bytewise_to_userspace(src, dst, small0_size); - - // Copy big - let big_src = src.add(small0_size); - let big_dst = dst.add(small0_size); - copy_quadwords(big_src, big_dst, big_size); - - // Copy small1 - let small1_src = src.add(big_size + small0_size); - let small1_dst = dst.add(big_size + small0_size); - copy_bytewise_to_userspace(small1_src, small1_dst, small1_size); - } + write_bytewise_to_userspace(src1, dst1, len1); + copy_quadwords(src2, dst2, len2); + write_bytewise_to_userspace(src3, dst3, len3); } } @@ -434,45 +431,33 @@ pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize) /// - https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00657.html /// - https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/stale-data-read-from-xapic.html pub(crate) unsafe fn copy_from_userspace(src: *const u8, dst: *mut u8, len: usize) { - // Copies memory region `src..src + len` to the enclave at `dst`. The source memory region - // is: - // - strictly less than 8 bytes in size and may be - // - located at a misaligned memory location - fn copy_misaligned_chunk_to_enclave(src: *const u8, dst: *mut u8, len: usize) { - let mut tmp_buff = [0u8; 16]; + /// Like `ptr::copy(src, dst, len)`, except it uses only u64-aligned reads. + /// + /// # Safety + /// The source memory region must not straddle an alignment boundary. + unsafe fn read_misaligned_from_userspace(src: *const u8, dst: *mut u8, len: usize) { + if intrinsics::likely(len == 0) { + return; + } unsafe { - // Compute an aligned memory region to read from - // +--------+ <-- aligned_src + aligned_len (8B-aligned) - // | pad1 | - // +--------+ <-- src + len (misaligned) - // | | - // | | - // | | - // +--------+ <-- src (misaligned) - // | pad0 | - // +--------+ <-- aligned_src (8B-aligned) - let pad0_size = src as usize % 8; - let aligned_src = src.sub(pad0_size); - - let pad1_size = 8 - (src.add(len) as usize % 8); - let aligned_len = pad0_size + len + pad1_size; - - debug_assert!(len < 8); - debug_assert_eq!(aligned_src as usize % 8, 0); - debug_assert_eq!(aligned_len % 8, 0); - debug_assert!(aligned_len <= 16); - - // Copy the aligned buffer to a temporary buffer - // Note: copying from a slightly different memory location is a bit odd. In this case it - // can't lead to page faults or inadvertent copying from the enclave as we only ensured - // that the `src` pointer is aligned at an 8 byte boundary. As pages are 4096 bytes - // aligned, `aligned_src` must be on the same page as `src`. A similar argument can be made - // for `src + len` - copy_quadwords(aligned_src as _, tmp_buff.as_mut_ptr(), aligned_len); - - // Copy the correct parts of the temporary buffer to the destination - ptr::copy(tmp_buff.as_ptr().add(pad0_size), dst, len); + let offset: usize; + let data: u64; + // doing a memory read that's potentially out of bounds for `src`, + // this isn't supported by Rust, so have to use assembly + asm!(" + movl {src:e}, {offset:e} + andl $7, {offset:e} + andq $-8, {src} + movq ({src}), {dst} + ", + src = inout(reg) src => _, + offset = out(reg) offset, + dst = out(reg) data, + options(nostack, att_syntax, readonly, pure) + ); + let data = data.to_le_bytes(); + ptr::copy_nonoverlapping(data.as_ptr().add(offset), dst, len); } } @@ -480,41 +465,19 @@ pub(crate) unsafe fn copy_from_userspace(src: *const u8, dst: *mut u8, len: usiz assert!(!dst.is_null()); assert!(is_user_range(src, len)); assert!(is_enclave_range(dst, len)); - assert!(!(src as usize).overflowing_add(len + 8).1); - assert!(!(dst as usize).overflowing_add(len + 8).1); + assert!(len < isize::MAX as usize); + assert!(!(src as usize).overflowing_add(len).1); + assert!(!(dst as usize).overflowing_add(len).1); - if len < 8 { - copy_misaligned_chunk_to_enclave(src, dst, len); - } else if len % 8 == 0 && src as usize % 8 == 0 { - // Copying 8-byte aligned quadwords: copy quad word per quad word - unsafe { - copy_quadwords(src, dst, len); - } - } else { - // Split copies into three parts: - // +--------+ - // | small0 | Chunk smaller than 8 bytes - // +--------+ - // | big | Chunk 8-byte aligned, and size a multiple of 8 bytes - // +--------+ - // | small1 | Chunk smaller than 8 bytes - // +--------+ - let (small0_size, big_size, small1_size) = region_as_aligned_chunks(dst, len); + unsafe { + let (len1, len2, len3) = u64_align_to_guaranteed(src, len); + let (src1, dst1) = (src, dst); + let (src2, dst2) = (src1.add(len1), dst1.add(len1)); + let (src3, dst3) = (src2.add(len2), dst2.add(len2)); - unsafe { - // Copy small0 - copy_misaligned_chunk_to_enclave(src, dst, small0_size); - - // Copy big - let big_src = src.add(small0_size); - let big_dst = dst.add(small0_size); - copy_quadwords(big_src, big_dst, big_size); - - // Copy small1 - let small1_src = src.add(big_size + small0_size); - let small1_dst = dst.add(big_size + small0_size); - copy_misaligned_chunk_to_enclave(small1_src, small1_dst, small1_size); - } + read_misaligned_from_userspace(src1, dst1, len1); + copy_quadwords(src2, dst2, len2); + read_misaligned_from_userspace(src3, dst3, len3); } } @@ -609,9 +572,9 @@ where /// Copies the value from user memory into enclave memory. pub fn to_enclave(&self) -> T { unsafe { - let mut data: T = mem::MaybeUninit::uninit().assume_init(); - copy_from_userspace(self.0.get() as _, &mut data as *mut T as _, mem::size_of::<T>()); - data + let mut data = mem::MaybeUninit::uninit(); + copy_from_userspace(self.0.get() as _, data.as_mut_ptr() as _, mem::size_of::<T>()); + data.assume_init() } } } diff --git a/library/std/src/sys/sgx/waitqueue/mod.rs b/library/std/src/sys/sgx/waitqueue/mod.rs index 5e1d859ee..25eca61d6 100644 --- a/library/std/src/sys/sgx/waitqueue/mod.rs +++ b/library/std/src/sys/sgx/waitqueue/mod.rs @@ -18,6 +18,7 @@ mod unsafe_list; use crate::num::NonZeroUsize; use crate::ops::{Deref, DerefMut}; +use crate::panic::{self, AssertUnwindSafe}; use crate::time::Duration; use super::abi::thread; @@ -147,7 +148,8 @@ impl WaitQueue { /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait /// until a wakeup event. /// - /// This function does not return until this thread has been awoken. + /// This function does not return until this thread has been awoken. When `before_wait` panics, + /// this function will abort. pub fn wait<T, F: FnOnce()>(mut guard: SpinMutexGuard<'_, WaitVariable<T>>, before_wait: F) { // very unsafe: check requirements of UnsafeList::push unsafe { @@ -157,8 +159,13 @@ impl WaitQueue { })); let entry = guard.queue.inner.push(&mut entry); drop(guard); - before_wait(); + if let Err(_e) = panic::catch_unwind(AssertUnwindSafe(|| before_wait())) { + rtabort!("Panic before wait on wakeup event") + } while !entry.lock().wake { + // `entry.wake` is only set in `notify_one` and `notify_all` functions. Both ensure + // the entry is removed from the queue _before_ setting this bool. There are no + // other references to `entry`. // don't panic, this would invalidate `entry` during unwinding let eventset = rtunwrap!(Ok, usercalls::wait(EV_UNPARK, WAIT_INDEFINITE)); rtassert!(eventset & EV_UNPARK == EV_UNPARK); @@ -169,6 +176,7 @@ impl WaitQueue { /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait /// until a wakeup event or timeout. If event was observed, returns true. /// If not, it will remove the calling thread from the wait queue. + /// When `before_wait` panics, this function will abort. pub fn wait_timeout<T, F: FnOnce()>( lock: &SpinMutex<WaitVariable<T>>, timeout: Duration, @@ -181,9 +189,13 @@ impl WaitQueue { wake: false, })); let entry_lock = lock.lock().queue.inner.push(&mut entry); - before_wait(); + if let Err(_e) = panic::catch_unwind(AssertUnwindSafe(|| before_wait())) { + rtabort!("Panic before wait on wakeup event or timeout") + } usercalls::wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake); - // acquire the wait queue's lock first to avoid deadlock. + // acquire the wait queue's lock first to avoid deadlock + // and ensure no other function can simultaneously access the list + // (e.g., `notify_one` or `notify_all`) let mut guard = lock.lock(); let success = entry_lock.lock().wake; if !success { @@ -204,8 +216,8 @@ impl WaitQueue { ) -> Result<WaitGuard<'_, T>, SpinMutexGuard<'_, WaitVariable<T>>> { // SAFETY: lifetime of the pop() return value is limited to the map // closure (The closure return value is 'static). The underlying - // stack frame won't be freed until after the WaitGuard created below - // is dropped. + // stack frame won't be freed until after the lock on the queue is released + // (i.e., `guard` is dropped). unsafe { let tcs = guard.queue.inner.pop().map(|entry| -> Tcs { let mut entry_guard = entry.lock(); @@ -231,7 +243,7 @@ impl WaitQueue { ) -> Result<WaitGuard<'_, T>, SpinMutexGuard<'_, WaitVariable<T>>> { // SAFETY: lifetime of the pop() return values are limited to the // while loop body. The underlying stack frames won't be freed until - // after the WaitGuard created below is dropped. + // after the lock on the queue is released (i.e., `guard` is dropped). unsafe { let mut count = 0; while let Some(entry) = guard.queue.inner.pop() { diff --git a/library/std/src/sys/solid/net.rs b/library/std/src/sys/solid/net.rs index 6adced787..1eae0fc06 100644 --- a/library/std/src/sys/solid/net.rs +++ b/library/std/src/sys/solid/net.rs @@ -233,12 +233,15 @@ impl Socket { } } + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + cvt(unsafe { netc::connect(self.0.raw(), addr.as_ptr(), len) })?; + Ok(()) + } + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { self.set_nonblocking(true)?; - let r = unsafe { - let (addr, len) = addr.into_inner(); - cvt(netc::connect(self.0.raw(), addr.as_ptr(), len)) - }; + let r = self.connect(addr); self.set_nonblocking(false)?; match r { diff --git a/library/std/src/sys/solid/thread_local_dtor.rs b/library/std/src/sys/solid/thread_local_dtor.rs index bad14bb37..26918a4fc 100644 --- a/library/std/src/sys/solid/thread_local_dtor.rs +++ b/library/std/src/sys/solid/thread_local_dtor.rs @@ -4,14 +4,13 @@ // Simplify dtor registration by using a list of destructors. use super::{abi, itron::task}; -use crate::cell::Cell; -use crate::mem; +use crate::cell::{Cell, RefCell}; #[thread_local] static REGISTERED: Cell<bool> = Cell::new(false); #[thread_local] -static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); +static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new()); pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { if !REGISTERED.get() { @@ -22,18 +21,20 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { REGISTERED.set(true); } - let list = unsafe { &mut DTORS }; - list.push((t, dtor)); + match DTORS.try_borrow_mut() { + Ok(mut dtors) => dtors.push((t, dtor)), + Err(_) => rtabort!("global allocator may not use TLS"), + } } pub unsafe fn run_dtors() { - let mut list = mem::take(unsafe { &mut DTORS }); + let mut list = DTORS.take(); while !list.is_empty() { for (ptr, dtor) in list { unsafe { dtor(ptr) }; } - list = mem::take(unsafe { &mut DTORS }); + list = DTORS.take(); } } diff --git a/library/std/src/sys/uefi/alloc.rs b/library/std/src/sys/uefi/alloc.rs index 789e3cbd8..ad3904d82 100644 --- a/library/std/src/sys/uefi/alloc.rs +++ b/library/std/src/sys/uefi/alloc.rs @@ -1,13 +1,17 @@ //! Global Allocator for UEFI. //! Uses [r-efi-alloc](https://crates.io/crates/r-efi-alloc) -use crate::alloc::{GlobalAlloc, Layout, System}; +use r_efi::protocols::loaded_image; -const MEMORY_TYPE: u32 = r_efi::efi::LOADER_DATA; +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::sync::OnceLock; +use crate::sys::uefi::helpers; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + static EFI_MEMORY_TYPE: OnceLock<u32> = OnceLock::new(); + // Return null pointer if boot services are not available if crate::os::uefi::env::boot_services().is_none() { return crate::ptr::null_mut(); @@ -15,8 +19,20 @@ unsafe impl GlobalAlloc for System { // If boot services is valid then SystemTable is not null. let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); + + // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this + // will never fail. + let mem_type = EFI_MEMORY_TYPE.get_or_init(|| { + let protocol = helpers::image_handle_protocol::<loaded_image::Protocol>( + loaded_image::PROTOCOL_GUID, + ) + .unwrap(); + // Gives allocations the memory type that the data sections were loaded as. + unsafe { (*protocol.as_ptr()).image_data_type } + }); + // The caller must ensure non-0 layout - unsafe { r_efi_alloc::raw::alloc(system_table, layout, MEMORY_TYPE) } + unsafe { r_efi_alloc::raw::alloc(system_table, layout, *mem_type) } } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { diff --git a/library/std/src/sys/uefi/args.rs b/library/std/src/sys/uefi/args.rs new file mode 100644 index 000000000..4ff7be748 --- /dev/null +++ b/library/std/src/sys/uefi/args.rs @@ -0,0 +1,158 @@ +use r_efi::protocols::loaded_image; + +use crate::env::current_exe; +use crate::ffi::OsString; +use crate::fmt; +use crate::iter::Iterator; +use crate::mem::size_of; +use crate::sys::uefi::helpers; +use crate::vec; + +pub struct Args { + parsed_args_list: vec::IntoIter<OsString>, +} + +pub fn args() -> Args { + let lazy_current_exe = || Vec::from([current_exe().map(Into::into).unwrap_or_default()]); + + // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this + // will never fail. + let protocol = + helpers::image_handle_protocol::<loaded_image::Protocol>(loaded_image::PROTOCOL_GUID) + .unwrap(); + + let lp_size = unsafe { (*protocol.as_ptr()).load_options_size } as usize; + // Break if we are sure that it cannot be UTF-16 + if lp_size < size_of::<u16>() || lp_size % size_of::<u16>() != 0 { + return Args { parsed_args_list: lazy_current_exe().into_iter() }; + } + let lp_size = lp_size / size_of::<u16>(); + + let lp_cmd_line = unsafe { (*protocol.as_ptr()).load_options as *const u16 }; + if !lp_cmd_line.is_aligned() { + return Args { parsed_args_list: lazy_current_exe().into_iter() }; + } + let lp_cmd_line = unsafe { crate::slice::from_raw_parts(lp_cmd_line, lp_size) }; + + Args { + parsed_args_list: parse_lp_cmd_line(lp_cmd_line) + .unwrap_or_else(lazy_current_exe) + .into_iter(), + } +} + +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.parsed_args_list.as_slice().fmt(f) + } +} + +impl Iterator for Args { + type Item = OsString; + + fn next(&mut self) -> Option<OsString> { + self.parsed_args_list.next() + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.parsed_args_list.size_hint() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.parsed_args_list.len() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option<OsString> { + self.parsed_args_list.next_back() + } +} + +/// Implements the UEFI command-line argument parsing algorithm. +/// +/// This implementation is based on what is defined in Section 3.4 of +/// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_Spec_2_0.pdf) +/// +/// Return None in the following cases: +/// - Invalid UTF-16 (unpaired surrogate) +/// - Empty/improper arguments +fn parse_lp_cmd_line(code_units: &[u16]) -> Option<Vec<OsString>> { + const QUOTE: char = '"'; + const SPACE: char = ' '; + const CARET: char = '^'; + const NULL: char = '\0'; + + let mut ret_val = Vec::new(); + let mut code_units_iter = char::decode_utf16(code_units.iter().cloned()).peekable(); + + // The executable name at the beginning is special. + let mut in_quotes = false; + let mut cur = String::new(); + while let Some(w) = code_units_iter.next() { + let w = w.ok()?; + match w { + // break on NULL + NULL => break, + // A quote mark always toggles `in_quotes` no matter what because + // there are no escape characters when parsing the executable name. + QUOTE => in_quotes = !in_quotes, + // If not `in_quotes` then whitespace ends argv[0]. + SPACE if !in_quotes => break, + // In all other cases the code unit is taken literally. + _ => cur.push(w), + } + } + + // If exe name is missing, the cli args are invalid + if cur.is_empty() { + return None; + } + + ret_val.push(OsString::from(cur)); + // Skip whitespace. + while code_units_iter.next_if_eq(&Ok(SPACE)).is_some() {} + + // Parse the arguments according to these rules: + // * All code units are taken literally except space, quote and caret. + // * When not `in_quotes`, space separate arguments. Consecutive spaces are + // treated as a single separator. + // * A space `in_quotes` is taken literally. + // * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally. + // * A quote can be escaped if preceded by caret. + // * A caret can be escaped if preceded by caret. + let mut cur = String::new(); + let mut in_quotes = false; + while let Some(w) = code_units_iter.next() { + let w = w.ok()?; + match w { + // break on NULL + NULL => break, + // If not `in_quotes`, a space or tab ends the argument. + SPACE if !in_quotes => { + ret_val.push(OsString::from(&cur[..])); + cur.truncate(0); + + // Skip whitespace. + while code_units_iter.next_if_eq(&Ok(SPACE)).is_some() {} + } + // Caret can escape quotes or carets + CARET if in_quotes => { + if let Some(x) = code_units_iter.next() { + cur.push(x.ok()?); + } + } + // If quote then flip `in_quotes` + QUOTE => in_quotes = !in_quotes, + // Everything else is always taken literally. + _ => cur.push(w), + } + } + // Push the final argument, if any. + if !cur.is_empty() || in_quotes { + ret_val.push(OsString::from(cur)); + } + Some(ret_val) +} diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs index 126661bfc..9837cc89f 100644 --- a/library/std/src/sys/uefi/helpers.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -139,3 +139,10 @@ pub(crate) unsafe fn close_event(evt: NonNull<crate::ffi::c_void>) -> io::Result if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } + +/// Get the Protocol for current system handle. +/// Note: Some protocols need to be manually freed. It is the callers responsibility to do so. +pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> Option<NonNull<T>> { + let system_handle = uefi::env::try_image_handle()?; + open_protocol(system_handle, protocol_guid).ok() +} diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index 9a10395af..4edc00e3e 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -13,7 +13,6 @@ //! [`OsString`]: crate::ffi::OsString pub mod alloc; -#[path = "../unsupported/args.rs"] pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; @@ -36,7 +35,6 @@ pub mod path; pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; -#[path = "../unsupported/stdio.rs"] pub mod stdio; #[path = "../unsupported/thread.rs"] pub mod thread; diff --git a/library/std/src/sys/uefi/stdio.rs b/library/std/src/sys/uefi/stdio.rs new file mode 100644 index 000000000..a533d8a05 --- /dev/null +++ b/library/std/src/sys/uefi/stdio.rs @@ -0,0 +1,162 @@ +use crate::io; +use crate::iter::Iterator; +use crate::mem::MaybeUninit; +use crate::os::uefi; +use crate::ptr::NonNull; + +const MAX_BUFFER_SIZE: usize = 8192; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +impl Stdin { + pub const fn new() -> Stdin { + Stdin + } +} + +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast(); + let stdin = unsafe { (*st.as_ptr()).con_in }; + + // Try reading any pending data + let inp = match read_key_stroke(stdin) { + Ok(x) => x, + Err(e) if e == r_efi::efi::Status::NOT_READY => { + // Wait for keypress for new data + wait_stdin(stdin)?; + read_key_stroke(stdin).map_err(|x| io::Error::from_raw_os_error(x.as_usize()))? + } + Err(e) => { + return Err(io::Error::from_raw_os_error(e.as_usize())); + } + }; + + // Check if the key is printiable character + if inp.scan_code != 0x00 { + return Err(io::const_io_error!(io::ErrorKind::Interrupted, "Special Key Press")); + } + + // SAFETY: Iterator will have only 1 character since we are reading only 1 Key + // SAFETY: This character will always be UCS-2 and thus no surrogates. + let ch: char = char::decode_utf16([inp.unicode_char]).next().unwrap().unwrap(); + if ch.len_utf8() > buf.len() { + return Ok(0); + } + + ch.encode_utf8(buf); + + Ok(ch.len_utf8()) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast(); + let stdout = unsafe { (*st.as_ptr()).con_out }; + + write(stdout, buf) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast(); + let stderr = unsafe { (*st.as_ptr()).std_err }; + + write(stderr, buf) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +// UCS-2 character should occupy 3 bytes at most in UTF-8 +pub const STDIN_BUF_SIZE: usize = 3; + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} + +pub fn panic_output() -> Option<impl io::Write> { + uefi::env::try_system_table().map(|_| Stderr::new()) +} + +fn write( + protocol: *mut r_efi::protocols::simple_text_output::Protocol, + buf: &[u8], +) -> io::Result<usize> { + let mut utf16 = [0; MAX_BUFFER_SIZE / 2]; + + // Get valid UTF-8 buffer + let utf8 = match crate::str::from_utf8(buf) { + Ok(x) => x, + Err(e) => unsafe { crate::str::from_utf8_unchecked(&buf[..e.valid_up_to()]) }, + }; + // Clip UTF-8 buffer to max UTF-16 buffer we support + let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len() - 1)]; + + for (i, ch) in utf8.encode_utf16().enumerate() { + utf16[i] = ch; + } + + unsafe { simple_text_output(protocol, &mut utf16) }?; + + Ok(utf8.len()) +} + +unsafe fn simple_text_output( + protocol: *mut r_efi::protocols::simple_text_output::Protocol, + buf: &mut [u16], +) -> io::Result<()> { + let res = unsafe { ((*protocol).output_string)(protocol, buf.as_mut_ptr()) }; + if res.is_error() { Err(io::Error::from_raw_os_error(res.as_usize())) } else { Ok(()) } +} + +fn wait_stdin(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> { + let boot_services: NonNull<r_efi::efi::BootServices> = + uefi::env::boot_services().unwrap().cast(); + let wait_for_event = unsafe { (*boot_services.as_ptr()).wait_for_event }; + let wait_for_key_event = unsafe { (*stdin).wait_for_key }; + + let r = { + let mut x: usize = 0; + (wait_for_event)(1, [wait_for_key_event].as_mut_ptr(), &mut x) + }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } +} + +fn read_key_stroke( + stdin: *mut r_efi::protocols::simple_text_input::Protocol, +) -> Result<r_efi::protocols::simple_text_input::InputKey, r_efi::efi::Status> { + let mut input_key: MaybeUninit<r_efi::protocols::simple_text_input::InputKey> = + MaybeUninit::uninit(); + + let r = unsafe { ((*stdin).read_key_stroke)(stdin, input_key.as_mut_ptr()) }; + + if r.is_error() { + Err(r) + } else { + let input_key = unsafe { input_key.assume_init() }; + Ok(input_key) + } +} diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index 19334e2af..2da17fabc 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -70,6 +70,7 @@ impl DoubleEndedIterator for Args { target_os = "redox", target_os = "vxworks", target_os = "horizon", + target_os = "aix", target_os = "nto", target_os = "hurd", ))] diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs index c6d8578a6..3bb492fa9 100644 --- a/library/std/src/sys/unix/env.rs +++ b/library/std/src/sys/unix/env.rs @@ -261,3 +261,14 @@ pub mod os { pub const EXE_SUFFIX: &str = ""; pub const EXE_EXTENSION: &str = ""; } + +#[cfg(target_os = "aix")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "aix"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".a"; + pub const DLL_EXTENSION: &str = "a"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index 6c4f40842..bf1fb3123 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -126,9 +126,17 @@ impl FileDesc { } pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { - #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "hurd")))] + #[cfg(not(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd" + )))] use libc::pread as pread64; - #[cfg(any(target_os = "linux", target_os = "android", target_os = "hurd"))] + #[cfg(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd" + ))] use libc::pread64; unsafe { @@ -285,9 +293,17 @@ impl FileDesc { } pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { - #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "hurd")))] + #[cfg(not(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd" + )))] use libc::pwrite as pwrite64; - #[cfg(any(target_os = "linux", target_os = "android", target_os = "hurd"))] + #[cfg(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd" + ))] use libc::pwrite64; unsafe { diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 764e1f257..40eb910fd 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -40,13 +40,17 @@ use libc::{c_int, mode_t}; ))] use libc::c_char; #[cfg(any( - target_os = "linux", + all(target_os = "linux", not(target_env = "musl")), target_os = "emscripten", target_os = "android", - target_os = "hurd", + target_os = "hurd" ))] use libc::dirfd; -#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "hurd"))] +#[cfg(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", + target_os = "hurd" +))] use libc::fstatat64; #[cfg(any( target_os = "android", @@ -54,11 +58,13 @@ use libc::fstatat64; target_os = "fuchsia", target_os = "redox", target_os = "illumos", + target_os = "aix", target_os = "nto", target_os = "vita", + all(target_os = "linux", target_env = "musl"), ))] use libc::readdir as readdir64; -#[cfg(any(target_os = "linux", target_os = "hurd"))] +#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] use libc::readdir64; #[cfg(any(target_os = "emscripten", target_os = "l4re"))] use libc::readdir64_r; @@ -71,6 +77,7 @@ use libc::readdir64_r; target_os = "l4re", target_os = "fuchsia", target_os = "redox", + target_os = "aix", target_os = "nto", target_os = "vita", target_os = "hurd", @@ -82,7 +89,7 @@ use libc::{ lstat as lstat64, off64_t, open as open64, stat as stat64, }; #[cfg(not(any( - target_os = "linux", + all(target_os = "linux", not(target_env = "musl")), target_os = "emscripten", target_os = "l4re", target_os = "android", @@ -93,7 +100,7 @@ use libc::{ lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, }; #[cfg(any( - target_os = "linux", + all(target_os = "linux", not(target_env = "musl")), target_os = "emscripten", target_os = "l4re", target_os = "hurd" @@ -288,6 +295,7 @@ unsafe impl Sync for Dir {} target_os = "illumos", target_os = "fuchsia", target_os = "redox", + target_os = "aix", target_os = "nto", target_os = "vita", target_os = "hurd", @@ -311,6 +319,7 @@ pub struct DirEntry { target_os = "illumos", target_os = "fuchsia", target_os = "redox", + target_os = "aix", target_os = "nto", target_os = "vita", target_os = "hurd", @@ -320,8 +329,9 @@ struct dirent64_min { #[cfg(not(any( target_os = "solaris", target_os = "illumos", + target_os = "aix", target_os = "nto", - target_os = "vita" + target_os = "vita", )))] d_type: u8, } @@ -333,6 +343,7 @@ struct dirent64_min { target_os = "illumos", target_os = "fuchsia", target_os = "redox", + target_os = "aix", target_os = "nto", target_os = "vita", target_os = "hurd", @@ -464,7 +475,22 @@ impl FileAttr { } } -#[cfg(not(any(target_os = "netbsd", target_os = "nto")))] +#[cfg(target_os = "aix")] +impl FileAttr { + pub fn modified(&self) -> io::Result<SystemTime> { + Ok(SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64)) + } + + pub fn accessed(&self) -> io::Result<SystemTime> { + Ok(SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64)) + } + + pub fn created(&self) -> io::Result<SystemTime> { + Ok(SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64)) + } +} + +#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix")))] impl FileAttr { #[cfg(not(any( target_os = "vxworks", @@ -671,6 +697,7 @@ impl Iterator for ReadDir { target_os = "fuchsia", target_os = "redox", target_os = "illumos", + target_os = "aix", target_os = "nto", target_os = "vita", target_os = "hurd", @@ -748,6 +775,7 @@ impl Iterator for ReadDir { #[cfg(not(any( target_os = "solaris", target_os = "illumos", + target_os = "aix", target_os = "nto", )))] d_type: *offset_ptr!(entry_ptr, d_type) as u8, @@ -772,6 +800,7 @@ impl Iterator for ReadDir { target_os = "fuchsia", target_os = "redox", target_os = "illumos", + target_os = "aix", target_os = "nto", target_os = "vita", target_os = "hurd", @@ -829,10 +858,10 @@ impl DirEntry { #[cfg(all( any( - target_os = "linux", + all(target_os = "linux", not(target_env = "musl")), target_os = "emscripten", target_os = "android", - target_os = "hurd", + target_os = "hurd" ), not(miri) ))] @@ -858,7 +887,7 @@ impl DirEntry { #[cfg(any( not(any( - target_os = "linux", + all(target_os = "linux", not(target_env = "musl")), target_os = "emscripten", target_os = "android", target_os = "hurd", @@ -874,6 +903,7 @@ impl DirEntry { target_os = "illumos", target_os = "haiku", target_os = "vxworks", + target_os = "aix", target_os = "nto", target_os = "vita", ))] @@ -886,6 +916,7 @@ impl DirEntry { target_os = "illumos", target_os = "haiku", target_os = "vxworks", + target_os = "aix", target_os = "nto", target_os = "vita", )))] @@ -920,6 +951,7 @@ impl DirEntry { target_os = "espidf", target_os = "horizon", target_os = "vita", + target_os = "aix", target_os = "nto", target_os = "hurd", ))] @@ -977,6 +1009,7 @@ impl DirEntry { target_os = "illumos", target_os = "fuchsia", target_os = "redox", + target_os = "aix", target_os = "nto", target_os = "vita", target_os = "hurd", @@ -991,6 +1024,7 @@ impl DirEntry { target_os = "illumos", target_os = "fuchsia", target_os = "redox", + target_os = "aix", target_os = "nto", target_os = "vita", target_os = "hurd", @@ -1391,6 +1425,7 @@ impl FromInner<FileDesc> for File { } impl AsFd for File { + #[inline] fn as_fd(&self) -> BorrowedFd<'_> { self.0.as_fd() } @@ -2025,6 +2060,7 @@ mod remove_dir_impl { target_os = "illumos", target_os = "haiku", target_os = "vxworks", + target_os = "aix", ))] fn is_dir(_ent: &DirEntry) -> Option<bool> { None @@ -2035,6 +2071,7 @@ mod remove_dir_impl { target_os = "illumos", target_os = "haiku", target_os = "vxworks", + target_os = "aix", )))] fn is_dir(ent: &DirEntry) -> Option<bool> { match ent.entry.d_type { diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 3edafde71..4b28f6feb 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -241,6 +241,7 @@ pub unsafe fn cleanup() { #[cfg(target_os = "android")] pub use crate::sys::android::signal; +#[allow(unused_imports)] #[cfg(not(target_os = "android"))] pub use libc::signal; @@ -278,6 +279,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { libc::ENETUNREACH => NetworkUnreachable, libc::ENOTCONN => NotConnected, libc::ENOTDIR => NotADirectory, + #[cfg(not(target_os = "aix"))] libc::ENOTEMPTY => DirectoryNotEmpty, libc::EPIPE => BrokenPipe, libc::EROFS => ReadOnlyFilesystem, @@ -413,7 +415,6 @@ cfg_if::cfg_if! { } else if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] { #[link(name = "System")] #[link(name = "objc")] - #[link(name = "Security", kind = "framework")] #[link(name = "Foundation", kind = "framework")] extern "C" {} } else if #[cfg(target_os = "fuchsia")] { diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index f450d708d..ec861f9cb 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -6,6 +6,7 @@ use crate::net::{Shutdown, SocketAddr}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::str; use crate::sys::fd::FileDesc; +use crate::sys::unix::IsMinusOne; use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; @@ -103,7 +104,7 @@ impl Socket { } } - #[cfg(not(any(target_os = "vxworks", target_os = "vita")))] + #[cfg(not(target_os = "vxworks"))] pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { unsafe { let mut fds = [0, 0]; @@ -135,11 +136,27 @@ impl Socket { } } - #[cfg(any(target_os = "vxworks", target_os = "vita"))] + #[cfg(target_os = "vxworks")] pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { unimplemented!() } + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + loop { + let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) }; + if result.is_minus_one() { + let err = crate::sys::os::errno(); + match err { + libc::EINTR => continue, + libc::EISCONN => return Ok(()), + _ => return Err(io::Error::from_raw_os_error(err)), + } + } + return Ok(()); + } + } + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { self.set_nonblocking(true)?; let r = unsafe { diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 01ff375d2..dc3c037c0 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -74,6 +74,7 @@ extern "C" { link_name = "__error" )] #[cfg_attr(target_os = "haiku", link_name = "_errnop")] + #[cfg_attr(target_os = "aix", link_name = "_Errno")] fn errno_location() -> *mut c_int; } @@ -254,6 +255,41 @@ impl StdError for JoinPathsError { } } +#[cfg(target_os = "aix")] +pub fn current_exe() -> io::Result<PathBuf> { + use crate::io::ErrorKind; + + #[cfg(test)] + use realstd::env; + + #[cfg(not(test))] + use crate::env; + + let exe_path = env::args().next().ok_or(io::const_io_error!( + ErrorKind::NotFound, + "an executable path was not found because no arguments were provided through argv" + ))?; + let path = PathBuf::from(exe_path); + if path.is_absolute() { + return path.canonicalize(); + } + // Search PWD to infer current_exe. + if let Some(pstr) = path.to_str() && pstr.contains("/") { + return getcwd().map(|cwd| cwd.join(path))?.canonicalize(); + } + // Search PATH to infer current_exe. + if let Some(p) = getenv(OsStr::from_bytes("PATH".as_bytes())) { + for search_path in split_paths(&p) { + let pb = search_path.join(&path); + if pb.is_file() && let Ok(metadata) = crate::fs::metadata(&pb) && + metadata.permissions().mode() & 0o111 != 0 { + return pb.canonicalize(); + } + } + } + Err(io::const_io_error!(ErrorKind::NotFound, "an executable path was not found")) +} + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] pub fn current_exe() -> io::Result<PathBuf> { unsafe { diff --git a/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs index 0cf163d9f..074f0a105 100644 --- a/library/std/src/sys/unix/process/mod.rs +++ b/library/std/src/sys/unix/process/mod.rs @@ -1,11 +1,13 @@ pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes}; pub use self::process_inner::{ExitStatus, ExitStatusError, Process}; pub use crate::ffi::OsString as EnvKey; -pub use crate::sys_common::process::CommandEnvs; #[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))] mod process_common; +#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] +mod process_unsupported; + cfg_if::cfg_if! { if #[cfg(target_os = "fuchsia")] { #[path = "process_fuchsia.rs"] @@ -15,8 +17,9 @@ cfg_if::cfg_if! { #[path = "process_vxworks.rs"] mod process_inner; } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] { - #[path = "process_unsupported.rs"] - mod process_inner; + mod process_inner { + pub use super::process_unsupported::*; + } } else { #[path = "process_unix.rs"] mod process_inner; diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index 1ca11a7f9..bac32d9e6 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -75,6 +75,7 @@ cfg_if::cfg_if! { return 0; } } else { + #[allow(unused_imports)] pub use libc::{sigemptyset, sigaddset}; } } diff --git a/library/std/src/sys/unix/process/process_common/tests.rs b/library/std/src/sys/unix/process/process_common/tests.rs index 03631e4e3..4e41efc90 100644 --- a/library/std/src/sys/unix/process/process_common/tests.rs +++ b/library/std/src/sys/unix/process/process_common/tests.rs @@ -159,3 +159,36 @@ fn test_program_kind() { ); } } + +// Test that Rust std handles wait status values (`ExitStatus`) the way that Unix does, +// at least for the values which represent a Unix exit status (`ExitCode`). +// Should work on every #[cfg(unix)] platform. However: +#[cfg(not(any( + // Fuchsia is not Unix and has totally broken std::os::unix. + // https://github.com/rust-lang/rust/issues/58590#issuecomment-836535609 + target_os = "fuchsia", +)))] +#[test] +fn unix_exit_statuses() { + use crate::num::NonZeroI32; + use crate::os::unix::process::ExitStatusExt; + use crate::process::*; + + for exit_code in 0..=0xff { + // FIXME impl From<ExitCode> for ExitStatus and then test that here too; + // the two ExitStatus values should be the same + let raw_wait_status = exit_code << 8; + let exit_status = ExitStatus::from_raw(raw_wait_status); + + assert_eq!(exit_status.code(), Some(exit_code)); + + if let Ok(nz) = NonZeroI32::try_from(exit_code) { + assert!(!exit_status.success()); + let es_error = exit_status.exit_ok().unwrap_err(); + assert_eq!(es_error.code().unwrap(), i32::from(nz)); + } else { + assert!(exit_status.success()); + assert_eq!(exit_status.exit_ok(), Ok(())); + } + } +} diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 564f8c482..72aca4e66 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -1074,3 +1074,8 @@ impl crate::os::linux::process::ChildExt for crate::process::Child { #[cfg(test)] #[path = "process_unix/tests.rs"] mod tests; + +// See [`process_unsupported_wait_status::compare_with_linux`]; +#[cfg(all(test, target_os = "linux"))] +#[path = "process_unsupported/wait_status.rs"] +mod process_unsupported_wait_status; diff --git a/library/std/src/sys/unix/process/process_unsupported.rs b/library/std/src/sys/unix/process/process_unsupported.rs index 8e0b971af..2fbb31922 100644 --- a/library/std/src/sys/unix/process/process_unsupported.rs +++ b/library/std/src/sys/unix/process/process_unsupported.rs @@ -55,68 +55,20 @@ impl Process { } } -#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] -pub struct ExitStatus(c_int); - -impl ExitStatus { - #[cfg_attr(target_os = "horizon", allow(unused))] - pub fn success(&self) -> bool { - self.code() == Some(0) - } - - pub fn exit_ok(&self) -> Result<(), ExitStatusError> { - Err(ExitStatusError(1.try_into().unwrap())) - } - - pub fn code(&self) -> Option<i32> { - None - } - - pub fn signal(&self) -> Option<i32> { - None - } - - pub fn core_dumped(&self) -> bool { - false - } - - pub fn stopped_signal(&self) -> Option<i32> { - None - } - - pub fn continued(&self) -> bool { - false - } - - pub fn into_raw(&self) -> c_int { - 0 - } -} - -/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. -impl From<c_int> for ExitStatus { - fn from(a: c_int) -> ExitStatus { - ExitStatus(a as i32) - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "exit code: {}", self.0) - } -} +mod wait_status; +pub use wait_status::ExitStatus; #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct ExitStatusError(NonZero_c_int); impl Into<ExitStatus> for ExitStatusError { fn into(self) -> ExitStatus { - ExitStatus(self.0.into()) + ExitStatus::from(c_int::from(self.0)) } } impl ExitStatusError { pub fn code(self) -> Option<NonZeroI32> { - ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) + ExitStatus::from(c_int::from(self.0)).code().map(|st| st.try_into().unwrap()) } } diff --git a/library/std/src/sys/unix/process/process_unsupported/wait_status.rs b/library/std/src/sys/unix/process/process_unsupported/wait_status.rs new file mode 100644 index 000000000..72b7ae18c --- /dev/null +++ b/library/std/src/sys/unix/process/process_unsupported/wait_status.rs @@ -0,0 +1,84 @@ +//! Emulated wait status for non-Unix #[cfg(unix) platforms +//! +//! Separate module to facilitate testing against a real Unix implementation. +use core::ffi::NonZero_c_int; + +use crate::ffi::c_int; +use crate::fmt; + +use super::ExitStatusError; + +/// Emulated wait status for use by `process_unsupported.rs` +/// +/// Uses the "traditional unix" encoding. For use on platfors which are `#[cfg(unix)]` +/// but do not actually support subprocesses at all. +/// +/// These platforms aren't Unix, but are simply pretending to be for porting convenience. +/// So, we provide a faithful pretence here. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] +pub struct ExitStatus { + wait_status: c_int, +} + +/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it +impl From<c_int> for ExitStatus { + fn from(wait_status: c_int) -> ExitStatus { + ExitStatus { wait_status } + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "emulated wait status: {}", self.wait_status) + } +} + +impl ExitStatus { + pub fn code(&self) -> Option<i32> { + // Linux and FreeBSD both agree that values linux 0x80 + // count as "WIFEXITED" even though this is quite mad. + // Likewise the macros disregard all the high bits, so are happy to declare + // out-of-range values to be WIFEXITED, WIFSTOPPED, etc. + let w = self.wait_status; + if (w & 0x7f) == 0 { Some((w & 0xff00) >> 8) } else { None } + } + + #[allow(unused)] + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is + // true on all actual versions of Unix, is widely assumed, and is specified in SuS + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not + // true for a platform pretending to be Unix, the tests (our doctests, and also + // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. + match NonZero_c_int::try_from(self.wait_status) { + /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), + /* was zero, couldn't convert */ Err(_) => Ok(()), + } + } + + pub fn signal(&self) -> Option<i32> { + let signal = self.wait_status & 0x007f; + if signal > 0 && signal < 0x7f { Some(signal) } else { None } + } + + pub fn core_dumped(&self) -> bool { + self.signal().is_some() && (self.wait_status & 0x80) != 0 + } + + pub fn stopped_signal(&self) -> Option<i32> { + let w = self.wait_status; + if (w & 0xff) == 0x7f { Some((w & 0xff00) >> 8) } else { None } + } + + pub fn continued(&self) -> bool { + self.wait_status == 0xffff + } + + pub fn into_raw(&self) -> c_int { + self.wait_status + } +} + +#[cfg(test)] +#[path = "wait_status/tests.rs"] // needed because of strange layout of process_unsupported +mod tests; diff --git a/library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs b/library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs new file mode 100644 index 000000000..5132eab10 --- /dev/null +++ b/library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs @@ -0,0 +1,36 @@ +// Note that tests in this file are run on Linux as well as on platforms using process_unsupported + +// Test that our emulation exactly matches Linux +// +// This test runs *on Linux* but it tests +// the implementation used on non-Unix `#[cfg(unix)]` platforms. +// +// I.e. we're using Linux as a proxy for "trad unix". +#[cfg(target_os = "linux")] +#[test] +fn compare_with_linux() { + use super::ExitStatus as Emulated; + use crate::os::unix::process::ExitStatusExt as _; + use crate::process::ExitStatus as Real; + + // Check that we handle out-of-range values similarly, too. + for wstatus in -0xf_ffff..0xf_ffff { + let emulated = Emulated::from(wstatus); + let real = Real::from_raw(wstatus); + + macro_rules! compare { { $method:ident } => { + assert_eq!( + emulated.$method(), + real.$method(), + "{wstatus:#x}.{}()", + stringify!($method), + ); + } } + compare!(code); + compare!(signal); + compare!(core_dumped); + compare!(stopped_signal); + compare!(continued); + compare!(into_raw); + } +} diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index fbf158f56..2825d1677 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -62,18 +62,15 @@ mod imp { unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) } } - #[cfg(any(target_os = "espidf", target_os = "horizon"))] + #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "freebsd"))] fn getrandom(buf: &mut [u8]) -> libc::ssize_t { - unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } - } - - #[cfg(target_os = "freebsd")] - fn getrandom(buf: &mut [u8]) -> libc::ssize_t { - // FIXME: using the above when libary std's libc is updated + #[cfg(not(target_os = "freebsd"))] + use libc::getrandom; + #[cfg(target_os = "freebsd")] extern "C" { fn getrandom( - buffer: *mut libc::c_void, - length: libc::size_t, + buf: *mut libc::c_void, + buflen: libc::size_t, flags: libc::c_uint, ) -> libc::ssize_t; } @@ -154,40 +151,65 @@ mod imp { } } -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] mod imp { - use crate::fs::File; - use crate::io::Read; - use crate::sys::os::errno; - use crate::sys::weak::weak; + use crate::io; use libc::{c_int, c_void, size_t}; - fn getentropy_fill_bytes(v: &mut [u8]) -> bool { - weak!(fn getentropy(*mut c_void, size_t) -> c_int); - - getentropy - .get() - .map(|f| { - // getentropy(2) permits a maximum buffer size of 256 bytes - 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()); - } - } - true - }) - .unwrap_or(false) + #[inline(always)] + fn random_failure() -> ! { + panic!("unexpected random generation error: {}", io::Error::last_os_error()); } - pub fn fill_bytes(v: &mut [u8]) { - if getentropy_fill_bytes(v) { - return; + #[cfg(target_os = "macos")] + fn getentropy_fill_bytes(v: &mut [u8]) { + extern "C" { + fn getentropy(bytes: *mut c_void, count: size_t) -> c_int; } - // 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") + // getentropy(2) permits a maximum buffer size of 256 bytes + for s in v.chunks_mut(256) { + let ret = unsafe { getentropy(s.as_mut_ptr().cast(), s.len()) }; + if ret == -1 { + random_failure() + } + } + } + + #[cfg(not(target_os = "macos"))] + fn ccrandom_fill_bytes(v: &mut [u8]) { + extern "C" { + fn CCRandomGenerateBytes(bytes: *mut c_void, count: size_t) -> c_int; + } + + let ret = unsafe { CCRandomGenerateBytes(v.as_mut_ptr().cast(), v.len()) }; + if ret == -1 { + random_failure() + } + } + + pub fn fill_bytes(v: &mut [u8]) { + // All supported versions of macOS (10.12+) support getentropy. + // + // `getentropy` is measurably faster (via Divan) then the other alternatives so its preferred + // when usable. + #[cfg(target_os = "macos")] + getentropy_fill_bytes(v); + + // On Apple platforms, `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply + // call into `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes` + // manages a CSPRNG which is seeded from the kernel's CSPRNG and which runs on + // its own thread accessed via GCD. This seems needlessly heavyweight for our purposes + // so we only use it on non-Mac OSes where the better entrypoints are blocked. + // + // `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible + // via `libSystem` (libc) while the other needs to link to `Security.framework`. + // + // Note that while `getentropy` has a available attribute in the macOS headers, the lack + // of a header in the iOS (and others) SDK means that its can cause app store rejections. + // Just use `CCRandomGenerateBytes` instead. + #[cfg(not(target_os = "macos"))] + ccrandom_fill_bytes(v); } } @@ -206,36 +228,7 @@ 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 = "tvos", 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()); - } - } -} - +// FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification. #[cfg(target_os = "netbsd")] mod imp { use crate::ptr; diff --git a/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs index 73c530786..3dbab4cc4 100644 --- a/library/std/src/sys/unix/stack_overflow.rs +++ b/library/std/src/sys/unix/stack_overflow.rs @@ -134,9 +134,19 @@ mod imp { // OpenBSD requires this flag for stack mapping // otherwise the said mapping will fail as a no-op on most systems // and has a different meaning on FreeBSD - #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",))] + #[cfg(any( + target_os = "openbsd", + target_os = "netbsd", + target_os = "linux", + target_os = "dragonfly", + ))] let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK; - #[cfg(not(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",)))] + #[cfg(not(any( + target_os = "openbsd", + target_os = "netbsd", + target_os = "linux", + target_os = "dragonfly", + )))] let flags = MAP_PRIVATE | MAP_ANON; let stackp = mmap64(ptr::null_mut(), SIGSTKSZ + page_size(), PROT_READ | PROT_WRITE, flags, -1, 0); diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 311ed9502..29db9468e 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -207,7 +207,9 @@ impl Thread { pub fn set_name(name: &CStr) { unsafe { let thread_self = libc::find_thread(ptr::null_mut()); - libc::rename_thread(thread_self, name.as_ptr()); + let res = libc::rename_thread(thread_self, name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, libc::B_OK); } } @@ -218,6 +220,7 @@ impl Thread { target_os = "redox", target_os = "vxworks", target_os = "hurd", + target_os = "aix", ))] pub fn set_name(_name: &CStr) { // Newlib, Emscripten, and VxWorks have no way to set a thread name. @@ -317,6 +320,7 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> { target_os = "macos", target_os = "solaris", target_os = "illumos", + target_os = "aix", ))] { #[allow(unused_assignments)] #[allow(unused_mut)] diff --git a/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs index fba2a676f..06399e8a2 100644 --- a/library/std/src/sys/unix/thread_local_dtor.rs +++ b/library/std/src/sys/unix/thread_local_dtor.rs @@ -11,28 +11,47 @@ // Note, however, that we run on lots older linuxes, as well as cross // compiling from a newer linux to an older linux, so we also have a // fallback implementation to use as well. +#[allow(unexpected_cfgs)] #[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "redox", target_os = "hurd"))] +// FIXME: The Rust compiler currently omits weakly function definitions (i.e., +// __cxa_thread_atexit_impl) and its metadata from LLVM IR. +#[no_sanitize(cfi, kcfi)] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::mem; use crate::sys_common::thread_local_dtor::register_dtor_fallback; + /// This is necessary because the __cxa_thread_atexit_impl implementation + /// std links to by default may be a C or C++ implementation that was not + /// compiled using the Clang integer normalization option. + #[cfg(not(sanitizer_cfi_normalize_integers))] + #[cfi_encoding = "i"] + #[repr(transparent)] + pub struct c_int(pub libc::c_int); + extern "C" { #[linkage = "extern_weak"] static __dso_handle: *mut u8; #[linkage = "extern_weak"] - static __cxa_thread_atexit_impl: *const libc::c_void; + static __cxa_thread_atexit_impl: Option< + extern "C" fn( + unsafe extern "C" fn(*mut libc::c_void), + *mut libc::c_void, + *mut libc::c_void, + ) -> c_int, + >; } - if !__cxa_thread_atexit_impl.is_null() { - type F = unsafe extern "C" fn( - dtor: unsafe extern "C" fn(*mut u8), - arg: *mut u8, - dso_handle: *mut u8, - ) -> libc::c_int; - mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)( - dtor, - t, - &__dso_handle as *const _ as *mut _, - ); + + if let Some(f) = __cxa_thread_atexit_impl { + unsafe { + f( + mem::transmute::< + unsafe extern "C" fn(*mut u8), + unsafe extern "C" fn(*mut libc::c_void), + >(dtor), + t.cast(), + &__dso_handle as *const _ as *mut _, + ); + } return; } register_dtor_fallback(t, dtor); @@ -48,17 +67,16 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { // workaround below is to register, via _tlv_atexit, a custom DTOR list once per // thread. thread_local dtors are pushed to the DTOR list without calling // _tlv_atexit. -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - use crate::cell::Cell; - use crate::mem; + use crate::cell::{Cell, RefCell}; use crate::ptr; #[thread_local] static REGISTERED: Cell<bool> = Cell::new(false); #[thread_local] - static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); + static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new()); if !REGISTERED.get() { _tlv_atexit(run_dtors, ptr::null_mut()); @@ -69,21 +87,28 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); } - let list = &mut DTORS; - list.push((t, dtor)); + match DTORS.try_borrow_mut() { + Ok(mut dtors) => dtors.push((t, dtor)), + Err(_) => rtabort!("global allocator may not use TLS"), + } unsafe extern "C" fn run_dtors(_: *mut u8) { - let mut list = mem::take(&mut DTORS); + let mut list = DTORS.take(); while !list.is_empty() { for (ptr, dtor) in list { dtor(ptr); } - list = mem::take(&mut DTORS); + list = DTORS.take(); } } } -#[cfg(any(target_os = "vxworks", target_os = "horizon", target_os = "emscripten"))] +#[cfg(any( + target_os = "vxworks", + target_os = "horizon", + target_os = "emscripten", + target_os = "aix" +))] #[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten) pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::sys_common::thread_local_dtor::register_dtor_fallback; diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index 4fe61b284..f2e86a4fb 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -1,8 +1,6 @@ use crate::fmt; use crate::time::Duration; -pub use self::inner::Instant; - const NSEC_PER_SEC: u64 = 1_000_000_000; pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; #[allow(dead_code)] // Used for pthread condvar timeouts @@ -40,6 +38,10 @@ impl SystemTime { SystemTime { t: Timespec::new(tv_sec, tv_nsec) } } + pub fn now() -> SystemTime { + SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) } + } + pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { self.t.sub_timespec(&other.t) } @@ -74,11 +76,65 @@ impl Timespec { } const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + // On Apple OS, dates before epoch are represented differently than on other + // Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1` + // and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and + // `nanoseconds=-900_000_000` on Apple OS. + // + // To compensate, we first detect this special case by checking if both + // seconds and nanoseconds are in range, and then correct the value for seconds + // and nanoseconds to match the common unix representation. + // + // Please note that Apple OS nonetheless accepts the standard unix format when + // setting file times, which makes this compensation round-trippable and generally + // transparent. + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos" + ))] + let (tv_sec, tv_nsec) = + if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) { + (tv_sec - 1, tv_nsec + 1_000_000_000) + } else { + (tv_sec, tv_nsec) + }; assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); // SAFETY: The assert above checks tv_nsec is within the valid range Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } } + pub fn now(clock: libc::clockid_t) -> Timespec { + use crate::mem::MaybeUninit; + use crate::sys::cvt; + + // Try to use 64-bit time in preparation for Y2038. + #[cfg(all( + target_os = "linux", + target_env = "gnu", + target_pointer_width = "32", + not(target_arch = "riscv32") + ))] + { + use crate::sys::weak::weak; + + // __clock_gettime64 was added to 32-bit arches in glibc 2.34, + // and it handles both vDSO calls and ENOSYS fallbacks itself. + weak!(fn __clock_gettime64(libc::clockid_t, *mut __timespec64) -> libc::c_int); + + if let Some(clock_gettime64) = __clock_gettime64.get() { + let mut t = MaybeUninit::uninit(); + cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); + return Timespec::from(unsafe { t.assume_init() }); + } + } + + let mut t = MaybeUninit::uninit(); + cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); + Timespec::from(unsafe { t.assume_init() }) + } + pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { if self >= other { // NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM @@ -216,209 +272,59 @@ impl From<__timespec64> for Timespec { } } -#[cfg(any( - all(target_os = "macos", any(not(target_arch = "aarch64"))), - target_os = "ios", - target_os = "watchos", - target_os = "tvos" -))] -mod inner { - use crate::sync::atomic::{AtomicU64, Ordering}; - use crate::sys::cvt; - use crate::sys_common::mul_div_u64; - use crate::time::Duration; - - use super::{SystemTime, Timespec, NSEC_PER_SEC}; - - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] - pub struct Instant { - t: u64, - } - - #[repr(C)] - #[derive(Copy, Clone)] - struct mach_timebase_info { - numer: u32, - denom: u32, - } - type mach_timebase_info_t = *mut mach_timebase_info; - type kern_return_t = libc::c_int; - - impl Instant { - pub fn now() -> Instant { - extern "C" { - fn mach_absolute_time() -> u64; - } - Instant { t: unsafe { mach_absolute_time() } } - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { - let diff = self.t.checked_sub(other.t)?; - let info = info(); - let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64); - Some(Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32)) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { - Some(Instant { t: self.t.checked_add(checked_dur2intervals(other)?)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { - Some(Instant { t: self.t.checked_sub(checked_dur2intervals(other)?)? }) - } - } - - impl SystemTime { - pub fn now() -> SystemTime { - use crate::ptr; - - let mut s = libc::timeval { tv_sec: 0, tv_usec: 0 }; - cvt(unsafe { libc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap(); - return SystemTime::from(s); - } - } - - impl From<libc::timeval> for Timespec { - fn from(t: libc::timeval) -> Timespec { - Timespec::new(t.tv_sec as i64, 1000 * t.tv_usec as i64) - } - } - - impl From<libc::timeval> for SystemTime { - fn from(t: libc::timeval) -> SystemTime { - SystemTime { t: Timespec::from(t) } - } - } - - fn checked_dur2intervals(dur: &Duration) -> Option<u64> { - let nanos = - dur.as_secs().checked_mul(NSEC_PER_SEC)?.checked_add(dur.subsec_nanos() as u64)?; - let info = info(); - Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64)) - } - - fn info() -> mach_timebase_info { - // INFO_BITS conceptually is an `Option<mach_timebase_info>`. We can do - // this in 64 bits because we know 0 is never a valid value for the - // `denom` field. - // - // Encoding this as a single `AtomicU64` allows us to use `Relaxed` - // operations, as we are only interested in the effects on a single - // memory location. - static INFO_BITS: AtomicU64 = AtomicU64::new(0); - - // If a previous thread has initialized `INFO_BITS`, use it. - let info_bits = INFO_BITS.load(Ordering::Relaxed); - if info_bits != 0 { - return info_from_bits(info_bits); - } - - // ... otherwise learn for ourselves ... - extern "C" { - fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t; - } - - let mut info = info_from_bits(0); - unsafe { - mach_timebase_info(&mut info); - } - INFO_BITS.store(info_to_bits(info), Ordering::Relaxed); - info - } - - #[inline] - fn info_to_bits(info: mach_timebase_info) -> u64 { - ((info.denom as u64) << 32) | (info.numer as u64) - } - - #[inline] - fn info_from_bits(bits: u64) -> mach_timebase_info { - mach_timebase_info { numer: bits as u32, denom: (bits >> 32) as u32 } - } +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Instant { + t: Timespec, } -#[cfg(not(any( - all(target_os = "macos", any(not(target_arch = "aarch64"))), - target_os = "ios", - target_os = "watchos", - target_os = "tvos" -)))] -mod inner { - use crate::fmt; - use crate::mem::MaybeUninit; - use crate::sys::cvt; - use crate::time::Duration; - - use super::{SystemTime, Timespec}; - - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct Instant { - t: Timespec, +impl Instant { + pub fn now() -> Instant { + // https://www.manpagez.com/man/3/clock_gettime/ + // + // CLOCK_UPTIME_RAW clock that increments monotonically, in the same man- + // ner as CLOCK_MONOTONIC_RAW, but that does not incre- + // ment while the system is asleep. The returned value + // is identical to the result of mach_absolute_time() + // after the appropriate mach_timebase conversion is + // applied. + // + // Instant on macos was historically implemented using mach_absolute_time; + // we preserve this value domain out of an abundance of caution. + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos" + ))] + const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW; + #[cfg(not(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos" + )))] + const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC; + Instant { t: Timespec::now(clock_id) } } - impl Instant { - pub fn now() -> Instant { - #[cfg(target_os = "macos")] - const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW; - #[cfg(not(target_os = "macos"))] - const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC; - Instant { t: Timespec::now(clock_id) } - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { - self.t.sub_timespec(&other.t).ok() - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { - Some(Instant { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { - Some(Instant { t: self.t.checked_sub_duration(other)? }) - } + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { + self.t.sub_timespec(&other.t).ok() } - impl fmt::Debug for Instant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Instant") - .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec.0) - .finish() - } + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_add_duration(other)? }) } - impl SystemTime { - pub fn now() -> SystemTime { - SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) } - } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_sub_duration(other)? }) } +} - impl Timespec { - pub fn now(clock: libc::clockid_t) -> Timespec { - // Try to use 64-bit time in preparation for Y2038. - #[cfg(all( - target_os = "linux", - target_env = "gnu", - target_pointer_width = "32", - not(target_arch = "riscv32") - ))] - { - use crate::sys::weak::weak; - - // __clock_gettime64 was added to 32-bit arches in glibc 2.34, - // and it handles both vDSO calls and ENOSYS fallbacks itself. - weak!(fn __clock_gettime64(libc::clockid_t, *mut super::__timespec64) -> libc::c_int); - - if let Some(clock_gettime64) = __clock_gettime64.get() { - let mut t = MaybeUninit::uninit(); - cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); - return Timespec::from(unsafe { t.assume_init() }); - } - } - - let mut t = MaybeUninit::uninit(); - cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); - Timespec::from(unsafe { t.assume_init() }) - } +impl fmt::Debug for Instant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Instant") + .field("tv_sec", &self.t.tv_sec) + .field("tv_nsec", &self.t.tv_nsec.0) + .finish() } } diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs index 5cbb5cb65..5919cc506 100644 --- a/library/std/src/sys/wasi/mod.rs +++ b/library/std/src/sys/wasi/mod.rs @@ -82,31 +82,99 @@ pub fn is_interrupted(errno: i32) -> bool { } pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { - use std_io::ErrorKind::*; - if errno > u16::MAX as i32 || errno < 0 { - return Uncategorized; + use std_io::ErrorKind; + + let Ok(errno) = u16::try_from(errno) else { + return ErrorKind::Uncategorized; + }; + + macro_rules! match_errno { + ($($($errno:ident)|+ => $errkind:ident),*, _ => $wildcard:ident $(,)?) => { + match errno { + $(e if $(e == ::wasi::$errno.raw())||+ => ErrorKind::$errkind),*, + _ => ErrorKind::$wildcard, + } + }; } - match errno { - e if e == wasi::ERRNO_CONNREFUSED.raw().into() => ConnectionRefused, - e if e == wasi::ERRNO_CONNRESET.raw().into() => ConnectionReset, - e if e == wasi::ERRNO_PERM.raw().into() || e == wasi::ERRNO_ACCES.raw().into() => { - PermissionDenied - } - e if e == wasi::ERRNO_PIPE.raw().into() => BrokenPipe, - e if e == wasi::ERRNO_NOTCONN.raw().into() => NotConnected, - e if e == wasi::ERRNO_CONNABORTED.raw().into() => ConnectionAborted, - e if e == wasi::ERRNO_ADDRNOTAVAIL.raw().into() => AddrNotAvailable, - e if e == wasi::ERRNO_ADDRINUSE.raw().into() => AddrInUse, - e if e == wasi::ERRNO_NOENT.raw().into() => NotFound, - e if e == wasi::ERRNO_INTR.raw().into() => Interrupted, - e if e == wasi::ERRNO_INVAL.raw().into() => InvalidInput, - e if e == wasi::ERRNO_TIMEDOUT.raw().into() => TimedOut, - e if e == wasi::ERRNO_EXIST.raw().into() => AlreadyExists, - e if e == wasi::ERRNO_AGAIN.raw().into() => WouldBlock, - e if e == wasi::ERRNO_NOSYS.raw().into() => Unsupported, - e if e == wasi::ERRNO_NOMEM.raw().into() => OutOfMemory, - _ => Uncategorized, + match_errno! { + ERRNO_2BIG => ArgumentListTooLong, + ERRNO_ACCES => PermissionDenied, + ERRNO_ADDRINUSE => AddrInUse, + ERRNO_ADDRNOTAVAIL => AddrNotAvailable, + ERRNO_AFNOSUPPORT => Unsupported, + ERRNO_AGAIN => WouldBlock, + // ALREADY => "connection already in progress", + // BADF => "bad file descriptor", + // BADMSG => "bad message", + ERRNO_BUSY => ResourceBusy, + // CANCELED => "operation canceled", + // CHILD => "no child processes", + ERRNO_CONNABORTED => ConnectionAborted, + ERRNO_CONNREFUSED => ConnectionRefused, + ERRNO_CONNRESET => ConnectionReset, + ERRNO_DEADLK => Deadlock, + // DESTADDRREQ => "destination address required", + ERRNO_DOM => InvalidInput, + // DQUOT => /* reserved */, + ERRNO_EXIST => AlreadyExists, + // FAULT => "bad address", + ERRNO_FBIG => FileTooLarge, + ERRNO_HOSTUNREACH => HostUnreachable, + // IDRM => "identifier removed", + // ILSEQ => "illegal byte sequence", + // INPROGRESS => "operation in progress", + ERRNO_INTR => Interrupted, + ERRNO_INVAL => InvalidInput, + ERRNO_IO => Uncategorized, + // ISCONN => "socket is connected", + ERRNO_ISDIR => IsADirectory, + ERRNO_LOOP => FilesystemLoop, + // MFILE => "file descriptor value too large", + ERRNO_MLINK => TooManyLinks, + // MSGSIZE => "message too large", + // MULTIHOP => /* reserved */, + ERRNO_NAMETOOLONG => InvalidFilename, + ERRNO_NETDOWN => NetworkDown, + // NETRESET => "connection aborted by network", + ERRNO_NETUNREACH => NetworkUnreachable, + // NFILE => "too many files open in system", + // NOBUFS => "no buffer space available", + ERRNO_NODEV => NotFound, + ERRNO_NOENT => NotFound, + // NOEXEC => "executable file format error", + // NOLCK => "no locks available", + // NOLINK => /* reserved */, + ERRNO_NOMEM => OutOfMemory, + // NOMSG => "no message of the desired type", + // NOPROTOOPT => "protocol not available", + ERRNO_NOSPC => StorageFull, + ERRNO_NOSYS => Unsupported, + ERRNO_NOTCONN => NotConnected, + ERRNO_NOTDIR => NotADirectory, + ERRNO_NOTEMPTY => DirectoryNotEmpty, + // NOTRECOVERABLE => "state not recoverable", + // NOTSOCK => "not a socket", + ERRNO_NOTSUP => Unsupported, + // NOTTY => "inappropriate I/O control operation", + ERRNO_NXIO => NotFound, + // OVERFLOW => "value too large to be stored in data type", + // OWNERDEAD => "previous owner died", + ERRNO_PERM => PermissionDenied, + ERRNO_PIPE => BrokenPipe, + // PROTO => "protocol error", + ERRNO_PROTONOSUPPORT => Unsupported, + // PROTOTYPE => "protocol wrong type for socket", + // RANGE => "result too large", + ERRNO_ROFS => ReadOnlyFilesystem, + ERRNO_SPIPE => NotSeekable, + ERRNO_SRCH => NotFound, + // STALE => /* reserved */, + ERRNO_TIMEDOUT => TimedOut, + ERRNO_TXTBSY => ResourceBusy, + ERRNO_XDEV => CrossesDevices, + ERRNO_NOTCAPABLE => PermissionDenied, + _ => Uncategorized, } } @@ -124,6 +192,7 @@ pub fn hashmap_random_keys() -> (u64, u64) { return ret; } +#[inline] fn err2io(err: wasi::Errno) -> std_io::Error { std_io::Error::from_raw_os_error(err.raw().into()) } diff --git a/library/std/src/sys/windows/api.rs b/library/std/src/sys/windows/api.rs new file mode 100644 index 000000000..e9f0bbfbe --- /dev/null +++ b/library/std/src/sys/windows/api.rs @@ -0,0 +1,157 @@ +//! # Safe(r) wrappers around Windows API functions. +//! +//! This module contains fairly thin wrappers around Windows API functions, +//! aimed at centralising safety instead of having unsafe blocks spread +//! throughout higher level code. This makes it much easier to audit FFI safety. +//! +//! Not all functions can be made completely safe without more context but in +//! such cases we should still endeavour to reduce the caller's burden of safety +//! as much as possible. +//! +//! ## Guidelines for wrappers +//! +//! Items here should be named similarly to their raw Windows API name, except +//! that they follow Rust's case conventions. E.g. function names are +//! lower_snake_case. The idea here is that it should be easy for a Windows +//! C/C++ programmer to identify the underlying function that's being wrapped +//! while not looking too out of place in Rust code. +//! +//! Every use of an `unsafe` block must have a related SAFETY comment, even if +//! it's trivially safe (for example, see `get_last_error`). Public unsafe +//! functions must document what the caller has to do to call them safely. +//! +//! Avoid unchecked `as` casts. For integers, either assert that the integer +//! is in range or use `try_into` instead. For pointers, prefer to use +//! `ptr.cast::<Type>()` when possible. +//! +//! This module must only depend on core and not on std types as the eventual +//! hope is to have std depend on sys and not the other way around. +//! However, some amount of glue code may currently be necessary so such code +//! should go in sys/windows/mod.rs rather than here. See `IoResult` as an example. + +use core::ffi::c_void; +use core::ptr::addr_of; + +use super::c; + +/// Helper method for getting the size of `T` as a u32. +/// Errors at compile time if the size would overflow. +/// +/// While a type larger than u32::MAX is unlikely, it is possible if only because of a bug. +/// However, one key motivation for this function is to avoid the temptation to +/// use frequent `as` casts. This is risky because they are too powerful. +/// For example, the following will compile today: +/// +/// `std::mem::size_of::<u64> as u32` +/// +/// Note that `size_of` is never actually called, instead a function pointer is +/// converted to a `u32`. Clippy would warn about this but, alas, it's not run +/// on the standard library. +const fn win32_size_of<T: Sized>() -> u32 { + // Const assert that the size is less than u32::MAX. + // Uses a trait to workaround restriction on using generic types in inner items. + trait Win32SizeOf: Sized { + const WIN32_SIZE_OF: u32 = { + let size = core::mem::size_of::<Self>(); + assert!(size <= u32::MAX as usize); + size as u32 + }; + } + impl<T: Sized> Win32SizeOf for T {} + + T::WIN32_SIZE_OF +} + +/// The `SetFileInformationByHandle` function takes a generic parameter by +/// making the user specify the type (class), a pointer to the data and its +/// size. This trait allows attaching that information to a Rust type so that +/// [`set_file_information_by_handle`] can be called safely. +/// +/// This trait is designed so that it can support variable sized types. +/// However, currently Rust's std only uses fixed sized structures. +/// +/// # Safety +/// +/// * `as_ptr` must return a pointer to memory that is readable up to `size` bytes. +/// * `CLASS` must accurately reflect the type pointed to by `as_ptr`. E.g. +/// the `FILE_BASIC_INFO` structure has the class `FileBasicInfo`. +pub unsafe trait SetFileInformation { + /// The type of information to set. + const CLASS: i32; + /// A pointer to the file information to set. + fn as_ptr(&self) -> *const c_void; + /// The size of the type pointed to by `as_ptr`. + fn size(&self) -> u32; +} +/// Helper trait for implementing `SetFileInformation` for statically sized types. +unsafe trait SizedSetFileInformation: Sized { + const CLASS: i32; +} +unsafe impl<T: SizedSetFileInformation> SetFileInformation for T { + const CLASS: i32 = T::CLASS; + fn as_ptr(&self) -> *const c_void { + addr_of!(*self).cast::<c_void>() + } + fn size(&self) -> u32 { + win32_size_of::<Self>() + } +} + +// SAFETY: FILE_BASIC_INFO, FILE_END_OF_FILE_INFO, FILE_ALLOCATION_INFO, +// FILE_DISPOSITION_INFO, FILE_DISPOSITION_INFO_EX and FILE_IO_PRIORITY_HINT_INFO +// are all plain `repr(C)` structs that only contain primitive types. +// The given information classes correctly match with the struct. +unsafe impl SizedSetFileInformation for c::FILE_BASIC_INFO { + const CLASS: i32 = c::FileBasicInfo; +} +unsafe impl SizedSetFileInformation for c::FILE_END_OF_FILE_INFO { + const CLASS: i32 = c::FileEndOfFileInfo; +} +unsafe impl SizedSetFileInformation for c::FILE_ALLOCATION_INFO { + const CLASS: i32 = c::FileAllocationInfo; +} +unsafe impl SizedSetFileInformation for c::FILE_DISPOSITION_INFO { + const CLASS: i32 = c::FileDispositionInfo; +} +unsafe impl SizedSetFileInformation for c::FILE_DISPOSITION_INFO_EX { + const CLASS: i32 = c::FileDispositionInfoEx; +} +unsafe impl SizedSetFileInformation for c::FILE_IO_PRIORITY_HINT_INFO { + const CLASS: i32 = c::FileIoPriorityHintInfo; +} + +#[inline] +pub fn set_file_information_by_handle<T: SetFileInformation>( + handle: c::HANDLE, + info: &T, +) -> Result<(), WinError> { + unsafe fn set_info( + handle: c::HANDLE, + class: i32, + info: *const c_void, + size: u32, + ) -> Result<(), WinError> { + let result = c::SetFileInformationByHandle(handle, class, info, size); + (result != 0).then_some(()).ok_or_else(|| get_last_error()) + } + // SAFETY: The `SetFileInformation` trait ensures that this is safe. + unsafe { set_info(handle, T::CLASS, info.as_ptr(), info.size()) } +} + +/// Gets the error from the last function. +/// This must be called immediately after the function that sets the error to +/// avoid the risk of another function overwriting it. +pub fn get_last_error() -> WinError { + // SAFETY: This just returns a thread-local u32 and has no other effects. + unsafe { WinError { code: c::GetLastError() } } +} + +/// An error code as returned by [`get_last_error`]. +/// +/// This is usually a 16-bit Win32 error code but may be a 32-bit HRESULT or NTSTATUS. +/// Check the documentation of the Windows API function being called for expected errors. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct WinError { + pub code: u32, +} diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index f3637cbb9..a349e24b0 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -46,6 +46,10 @@ pub use FD_SET as fd_set; pub use LINGER as linger; pub use TIMEVAL as timeval; +// https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure?view=msvc-170 +pub const EXIT_SUCCESS: u32 = 0; +pub const EXIT_FAILURE: u32 = 1; + pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { Ptr: ptr::null_mut() }; pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { Ptr: ptr::null_mut() }; pub const INIT_ONCE_STATIC_INIT: INIT_ONCE = INIT_ONCE { Ptr: ptr::null_mut() }; diff --git a/library/std/src/sys/windows/c/windows_sys.lst b/library/std/src/sys/windows/c/windows_sys.lst index 0aca37e2d..38bf15b7c 100644 --- a/library/std/src/sys/windows/c/windows_sys.lst +++ b/library/std/src/sys/windows/c/windows_sys.lst @@ -1964,6 +1964,7 @@ Windows.Win32.Networking.WinSock.ADDRESS_FAMILY Windows.Win32.Networking.WinSock.ADDRINFOA Windows.Win32.Networking.WinSock.AF_INET Windows.Win32.Networking.WinSock.AF_INET6 +Windows.Win32.Networking.WinSock.AF_UNIX Windows.Win32.Networking.WinSock.AF_UNSPEC Windows.Win32.Networking.WinSock.bind Windows.Win32.Networking.WinSock.closesocket @@ -2058,6 +2059,7 @@ Windows.Win32.Networking.WinSock.SOCK_RDM Windows.Win32.Networking.WinSock.SOCK_SEQPACKET Windows.Win32.Networking.WinSock.SOCK_STREAM Windows.Win32.Networking.WinSock.SOCKADDR +Windows.Win32.Networking.WinSock.SOCKADDR_UN Windows.Win32.Networking.WinSock.SOCKET Windows.Win32.Networking.WinSock.SOCKET_ERROR Windows.Win32.Networking.WinSock.SOL_SOCKET @@ -2222,6 +2224,7 @@ Windows.Win32.Storage.FileSystem.FILE_ACCESS_RIGHTS Windows.Win32.Storage.FileSystem.FILE_ADD_FILE Windows.Win32.Storage.FileSystem.FILE_ADD_SUBDIRECTORY Windows.Win32.Storage.FileSystem.FILE_ALL_ACCESS +Windows.Win32.Storage.FileSystem.FILE_ALLOCATION_INFO Windows.Win32.Storage.FileSystem.FILE_APPEND_DATA Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_ARCHIVE Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_COMPRESSED @@ -2282,6 +2285,7 @@ Windows.Win32.Storage.FileSystem.FILE_GENERIC_READ Windows.Win32.Storage.FileSystem.FILE_GENERIC_WRITE Windows.Win32.Storage.FileSystem.FILE_ID_BOTH_DIR_INFO Windows.Win32.Storage.FileSystem.FILE_INFO_BY_HANDLE_CLASS +Windows.Win32.Storage.FileSystem.FILE_IO_PRIORITY_HINT_INFO Windows.Win32.Storage.FileSystem.FILE_LIST_DIRECTORY Windows.Win32.Storage.FileSystem.FILE_NAME_NORMALIZED Windows.Win32.Storage.FileSystem.FILE_NAME_OPENED @@ -2503,9 +2507,12 @@ Windows.Win32.System.Threading.CREATE_SEPARATE_WOW_VDM Windows.Win32.System.Threading.CREATE_SHARED_WOW_VDM Windows.Win32.System.Threading.CREATE_SUSPENDED Windows.Win32.System.Threading.CREATE_UNICODE_ENVIRONMENT +Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_HIGH_RESOLUTION +Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_MANUAL_RESET Windows.Win32.System.Threading.CreateEventW Windows.Win32.System.Threading.CreateProcessW Windows.Win32.System.Threading.CreateThread +Windows.Win32.System.Threading.CreateWaitableTimerExW Windows.Win32.System.Threading.DEBUG_ONLY_THIS_PROCESS Windows.Win32.System.Threading.DEBUG_PROCESS Windows.Win32.System.Threading.DeleteProcThreadAttributeList @@ -2542,6 +2549,7 @@ Windows.Win32.System.Threading.REALTIME_PRIORITY_CLASS Windows.Win32.System.Threading.ReleaseSRWLockExclusive Windows.Win32.System.Threading.ReleaseSRWLockShared Windows.Win32.System.Threading.SetThreadStackGuarantee +Windows.Win32.System.Threading.SetWaitableTimer Windows.Win32.System.Threading.Sleep Windows.Win32.System.Threading.SleepConditionVariableSRW Windows.Win32.System.Threading.SleepEx @@ -2568,6 +2576,8 @@ Windows.Win32.System.Threading.TerminateProcess Windows.Win32.System.Threading.THREAD_CREATE_RUN_IMMEDIATELY Windows.Win32.System.Threading.THREAD_CREATE_SUSPENDED Windows.Win32.System.Threading.THREAD_CREATION_FLAGS +Windows.Win32.System.Threading.TIMER_ALL_ACCESS +Windows.Win32.System.Threading.TIMER_MODIFY_STATE Windows.Win32.System.Threading.TLS_OUT_OF_INDEXES Windows.Win32.System.Threading.TlsAlloc Windows.Win32.System.Threading.TlsFree diff --git a/library/std/src/sys/windows/c/windows_sys.rs b/library/std/src/sys/windows/c/windows_sys.rs index 851d15915..e0509e6a5 100644 --- a/library/std/src/sys/windows/c/windows_sys.rs +++ b/library/std/src/sys/windows/c/windows_sys.rs @@ -152,6 +152,15 @@ extern "system" { } #[link(name = "kernel32")] extern "system" { + pub fn CreateWaitableTimerExW( + lptimerattributes: *const SECURITY_ATTRIBUTES, + lptimername: PCWSTR, + dwflags: u32, + dwdesiredaccess: u32, + ) -> HANDLE; +} +#[link(name = "kernel32")] +extern "system" { pub fn DeleteFileW(lpfilename: PCWSTR) -> BOOL; } #[link(name = "kernel32")] @@ -509,6 +518,17 @@ extern "system" { } #[link(name = "kernel32")] extern "system" { + pub fn SetWaitableTimer( + htimer: HANDLE, + lpduetime: *const i64, + lperiod: i32, + pfncompletionroutine: PTIMERAPCROUTINE, + lpargtocompletionroutine: *const ::core::ffi::c_void, + fresume: BOOL, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { pub fn Sleep(dwmilliseconds: u32) -> (); } #[link(name = "kernel32")] @@ -847,6 +867,7 @@ impl ::core::clone::Clone for ADDRINFOA { } pub const AF_INET: ADDRESS_FAMILY = 2u16; pub const AF_INET6: ADDRESS_FAMILY = 23u16; +pub const AF_UNIX: u16 = 1u16; pub const AF_UNSPEC: ADDRESS_FAMILY = 0u16; pub const ALL_PROCESSOR_GROUPS: u32 = 65535u32; #[repr(C)] @@ -1164,6 +1185,8 @@ pub const CREATE_SEPARATE_WOW_VDM: PROCESS_CREATION_FLAGS = 2048u32; pub const CREATE_SHARED_WOW_VDM: PROCESS_CREATION_FLAGS = 4096u32; pub const CREATE_SUSPENDED: PROCESS_CREATION_FLAGS = 4u32; pub const CREATE_UNICODE_ENVIRONMENT: PROCESS_CREATION_FLAGS = 1024u32; +pub const CREATE_WAITABLE_TIMER_HIGH_RESOLUTION: u32 = 2u32; +pub const CREATE_WAITABLE_TIMER_MANUAL_RESET: u32 = 1u32; pub const CSTR_EQUAL: COMPARESTRING_RESULT = 2i32; pub const CSTR_GREATER_THAN: COMPARESTRING_RESULT = 3i32; pub const CSTR_LESS_THAN: COMPARESTRING_RESULT = 1i32; @@ -3106,6 +3129,16 @@ impl ::core::clone::Clone for FILETIME { pub type FILE_ACCESS_RIGHTS = u32; pub const FILE_ADD_FILE: FILE_ACCESS_RIGHTS = 2u32; pub const FILE_ADD_SUBDIRECTORY: FILE_ACCESS_RIGHTS = 4u32; +#[repr(C)] +pub struct FILE_ALLOCATION_INFO { + pub AllocationSize: i64, +} +impl ::core::marker::Copy for FILE_ALLOCATION_INFO {} +impl ::core::clone::Clone for FILE_ALLOCATION_INFO { + fn clone(&self) -> Self { + *self + } +} pub const FILE_ALL_ACCESS: FILE_ACCESS_RIGHTS = 2032127u32; pub const FILE_APPEND_DATA: FILE_ACCESS_RIGHTS = 4u32; pub const FILE_ATTRIBUTE_ARCHIVE: FILE_FLAGS_AND_ATTRIBUTES = 32u32; @@ -3247,6 +3280,16 @@ impl ::core::clone::Clone for FILE_ID_BOTH_DIR_INFO { } } pub type FILE_INFO_BY_HANDLE_CLASS = i32; +#[repr(C)] +pub struct FILE_IO_PRIORITY_HINT_INFO { + pub PriorityHint: PRIORITY_HINT, +} +impl ::core::marker::Copy for FILE_IO_PRIORITY_HINT_INFO {} +impl ::core::clone::Clone for FILE_IO_PRIORITY_HINT_INFO { + fn clone(&self) -> Self { + *self + } +} pub const FILE_LIST_DIRECTORY: FILE_ACCESS_RIGHTS = 1u32; pub const FILE_NAME_NORMALIZED: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32; pub const FILE_NAME_OPENED: GETFINALPATHNAMEBYHANDLE_FLAGS = 8u32; @@ -3752,6 +3795,7 @@ pub const PIPE_SERVER_END: NAMED_PIPE_MODE = 1u32; pub const PIPE_TYPE_BYTE: NAMED_PIPE_MODE = 0u32; pub const PIPE_TYPE_MESSAGE: NAMED_PIPE_MODE = 4u32; pub const PIPE_WAIT: NAMED_PIPE_MODE = 0u32; +pub type PRIORITY_HINT = i32; pub type PROCESSOR_ARCHITECTURE = u16; pub type PROCESS_CREATION_FLAGS = u32; #[repr(C)] @@ -3774,6 +3818,13 @@ pub const PROFILE_SERVER: PROCESS_CREATION_FLAGS = 1073741824u32; pub const PROFILE_USER: PROCESS_CREATION_FLAGS = 268435456u32; pub const PROGRESS_CONTINUE: u32 = 0u32; pub type PSTR = *mut u8; +pub type PTIMERAPCROUTINE = ::core::option::Option< + unsafe extern "system" fn( + lpargtocompletionroutine: *const ::core::ffi::c_void, + dwtimerlowvalue: u32, + dwtimerhighvalue: u32, + ) -> (), +>; pub type PWSTR = *mut u16; pub const READ_CONTROL: FILE_ACCESS_RIGHTS = 131072u32; pub const REALTIME_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 256u32; @@ -3813,6 +3864,17 @@ impl ::core::clone::Clone for SOCKADDR { *self } } +#[repr(C)] +pub struct SOCKADDR_UN { + pub sun_family: ADDRESS_FAMILY, + pub sun_path: [u8; 108], +} +impl ::core::marker::Copy for SOCKADDR_UN {} +impl ::core::clone::Clone for SOCKADDR_UN { + fn clone(&self) -> Self { + *self + } +} pub type SOCKET = usize; pub const SOCKET_ERROR: i32 = -1i32; pub const SOCK_DGRAM: WINSOCK_SOCKET_TYPE = 2i32; @@ -3910,6 +3972,7 @@ pub type SYMBOLIC_LINK_FLAGS = u32; pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: SYMBOLIC_LINK_FLAGS = 2u32; pub const SYMBOLIC_LINK_FLAG_DIRECTORY: SYMBOLIC_LINK_FLAGS = 1u32; pub const SYMLINK_FLAG_RELATIVE: u32 = 1u32; +pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32; pub const SYNCHRONIZE: FILE_ACCESS_RIGHTS = 1048576u32; #[repr(C)] pub struct SYSTEM_INFO { @@ -3956,6 +4019,8 @@ pub const TCP_NODELAY: i32 = 1i32; pub const THREAD_CREATE_RUN_IMMEDIATELY: THREAD_CREATION_FLAGS = 0u32; pub const THREAD_CREATE_SUSPENDED: THREAD_CREATION_FLAGS = 4u32; pub type THREAD_CREATION_FLAGS = u32; +pub const TIMER_ALL_ACCESS: SYNCHRONIZATION_ACCESS_RIGHTS = 2031619u32; +pub const TIMER_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32; #[repr(C)] pub struct TIMEVAL { pub tv_sec: i32, diff --git a/library/std/src/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs index 1b2a86f3c..36578d5a3 100644 --- a/library/std/src/sys/windows/cmath.rs +++ b/library/std/src/sys/windows/cmath.rs @@ -1,6 +1,6 @@ #![cfg(not(test))] -use libc::{c_double, c_float, c_int}; +use core::ffi::{c_double, c_float, c_int}; extern "C" { pub fn acos(n: c_double) -> c_double; @@ -33,7 +33,7 @@ pub use self::shims::*; #[cfg(not(all(target_env = "msvc", target_arch = "x86")))] mod shims { - use libc::c_float; + use core::ffi::c_float; extern "C" { pub fn acosf(n: c_float) -> c_float; @@ -52,7 +52,7 @@ mod shims { // back to f32. While not precisely correct should be "correct enough" for now. #[cfg(all(target_env = "msvc", target_arch = "x86"))] mod shims { - use libc::c_float; + use core::ffi::c_float; #[inline] pub unsafe fn acosf(n: c_float) -> c_float { diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 21a65bc25..d7e36b9a3 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -16,8 +16,10 @@ use crate::sys::{c, cvt, Align8}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::thread; +use core::ffi::c_void; + use super::path::maybe_verbatim; -use super::to_u16s; +use super::{api, to_u16s, IoResult}; pub struct File { handle: Handle, @@ -121,7 +123,7 @@ impl Iterator for ReadDir { let mut wfd = mem::zeroed(); loop { if c::FindNextFileW(self.handle.0, &mut wfd) == 0 { - if c::GetLastError() == c::ERROR_NO_MORE_FILES { + if api::get_last_error().code == c::ERROR_NO_MORE_FILES { return None; } else { return Some(Err(Error::last_os_error())); @@ -316,17 +318,8 @@ impl File { } pub fn truncate(&self, size: u64) -> io::Result<()> { - let mut info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as c::LARGE_INTEGER }; - let size = mem::size_of_val(&info); - cvt(unsafe { - c::SetFileInformationByHandle( - self.handle.as_raw_handle(), - c::FileEndOfFileInfo, - &mut info as *mut _ as *mut _, - size as c::DWORD, - ) - })?; - Ok(()) + let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 }; + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() } #[cfg(not(target_vendor = "uwp"))] @@ -371,7 +364,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileBasicInfo, - &mut info as *mut _ as *mut libc::c_void, + &mut info as *mut _ as *mut c_void, size as c::DWORD, ))?; let mut attr = FileAttr { @@ -399,7 +392,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileStandardInfo, - &mut info as *mut _ as *mut libc::c_void, + &mut info as *mut _ as *mut c_void, size as c::DWORD, ))?; attr.file_size = info.AllocationSize as u64; @@ -563,23 +556,14 @@ impl File { } pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { - let mut info = c::FILE_BASIC_INFO { + let info = c::FILE_BASIC_INFO { CreationTime: 0, LastAccessTime: 0, LastWriteTime: 0, ChangeTime: 0, FileAttributes: perm.attrs, }; - let size = mem::size_of_val(&info); - cvt(unsafe { - c::SetFileInformationByHandle( - self.handle.as_raw_handle(), - c::FileBasicInfo, - &mut info as *mut _ as *mut _, - size as c::DWORD, - ) - })?; - Ok(()) + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() } pub fn set_times(&self, times: FileTimes) -> io::Result<()> { @@ -624,7 +608,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileBasicInfo, - &mut info as *mut _ as *mut libc::c_void, + &mut info as *mut _ as *mut c_void, size as c::DWORD, ))?; Ok(info) @@ -639,38 +623,20 @@ impl File { /// If the operation is not supported for this filesystem or OS version /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`. fn posix_delete(&self) -> io::Result<()> { - let mut info = c::FILE_DISPOSITION_INFO_EX { + let info = c::FILE_DISPOSITION_INFO_EX { Flags: c::FILE_DISPOSITION_FLAG_DELETE | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE, }; - let size = mem::size_of_val(&info); - cvt(unsafe { - c::SetFileInformationByHandle( - self.handle.as_raw_handle(), - c::FileDispositionInfoEx, - &mut info as *mut _ as *mut _, - size as c::DWORD, - ) - })?; - Ok(()) + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() } /// Delete a file using win32 semantics. The file won't actually be deleted /// until all file handles are closed. However, marking a file for deletion /// will prevent anyone from opening a new handle to the file. fn win32_delete(&self) -> io::Result<()> { - let mut info = c::FILE_DISPOSITION_INFO { DeleteFile: c::TRUE as _ }; - let size = mem::size_of_val(&info); - cvt(unsafe { - c::SetFileInformationByHandle( - self.handle.as_raw_handle(), - c::FileDispositionInfo, - &mut info as *mut _ as *mut _, - size as c::DWORD, - ) - })?; - Ok(()) + let info = c::FILE_DISPOSITION_INFO { DeleteFile: c::TRUE as _ }; + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() } /// Fill the given buffer with as many directory entries as will fit. @@ -1064,6 +1030,14 @@ impl DirBuilder { } pub fn readdir(p: &Path) -> io::Result<ReadDir> { + // We push a `*` to the end of the path which cause the empty path to be + // treated as the current directory. So, for consistency with other platforms, + // we explicitly error on the empty path. + if p.as_os_str().is_empty() { + // Return an error code consistent with other ways of opening files. + // E.g. fs::metadata or File::open. + return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32)); + } let root = p.to_path_buf(); let star = p.join("*"); let path = maybe_verbatim(&star)?; @@ -1513,6 +1487,13 @@ pub fn try_exists(path: &Path) -> io::Result<bool> { // as the file existing. _ if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => Ok(true), + // `ERROR_CANT_ACCESS_FILE` means that a file exists but that the + // reparse point could not be handled by `CreateFile`. + // This can happen for special files such as: + // * Unix domain sockets which you need to `connect` to + // * App exec links which require using `CreateProcess` + _ if e.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => Ok(true), + // Other errors such as `ERROR_ACCESS_DENIED` may indicate that the // file exists. However, these types of errors are usually more // permanent so we report them here. diff --git a/library/std/src/sys/windows/io.rs b/library/std/src/sys/windows/io.rs index fc9856cae..9b540ee07 100644 --- a/library/std/src/sys/windows/io.rs +++ b/library/std/src/sys/windows/io.rs @@ -3,7 +3,7 @@ use crate::mem::size_of; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; use crate::slice; use crate::sys::c; -use libc; +use core::ffi::c_void; #[derive(Copy, Clone)] #[repr(transparent)] @@ -136,7 +136,7 @@ unsafe fn msys_tty_on(handle: c::HANDLE) -> bool { let res = c::GetFileInformationByHandleEx( handle, c::FileNameInfo, - &mut name_info as *mut _ as *mut libc::c_void, + &mut name_info as *mut _ as *mut c_void, size_of::<FILE_NAME_INFO>() as u32, ); if res == 0 { diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index b609ad247..c4e56e13b 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -44,6 +44,18 @@ cfg_if::cfg_if! { } } +mod api; + +/// Map a Result<T, WinError> to io::Result<T>. +trait IoResult<T> { + fn io_result(self) -> crate::io::Result<T>; +} +impl<T> IoResult<T> for Result<T, api::WinError> { + fn io_result(self) -> crate::io::Result<T> { + self.map_err(|e| crate::io::Error::from_raw_os_error(e.code as i32)) + } +} + // 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, _sigpipe: u8) { @@ -241,11 +253,11 @@ where // not an actual error. c::SetLastError(0); let k = match f1(buf.as_mut_ptr().cast::<u16>(), n as c::DWORD) { - 0 if c::GetLastError() == 0 => 0, + 0 if api::get_last_error().code == 0 => 0, 0 => return Err(crate::io::Error::last_os_error()), n => n, } as usize; - if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER { + if k == n && api::get_last_error().code == c::ERROR_INSUFFICIENT_BUFFER { n = n.saturating_mul(2).min(c::DWORD::MAX as usize); } else if k > n { n = k; diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index abdcab424..c29b86366 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -15,7 +15,7 @@ use crate::sys_common::net; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; -use libc::{c_int, c_long, c_ulong, c_ushort}; +use core::ffi::{c_int, c_long, c_ulong, c_ushort}; pub type wrlen_t = i32; @@ -140,13 +140,15 @@ impl Socket { } } + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) }; + cvt(result).map(drop) + } + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { self.set_nonblocking(true)?; - let result = { - let (addr, len) = addr.into_inner(); - let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) }; - cvt(result).map(drop) - }; + let result = self.connect(addr); self.set_nonblocking(false)?; match result { diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs index 58afca088..8cc905101 100644 --- a/library/std/src/sys/windows/os.rs +++ b/library/std/src/sys/windows/os.rs @@ -17,10 +17,10 @@ use crate::ptr; use crate::slice; use crate::sys::{c, cvt}; -use super::to_u16s; +use super::{api, to_u16s}; pub fn errno() -> i32 { - unsafe { c::GetLastError() as i32 } + api::get_last_error().code as i32 } /// Gets a detailed string description for the given error number. @@ -336,7 +336,7 @@ fn home_dir_crt() -> Option<PathBuf> { super::fill_utf16_buf( |buf, mut sz| { match c::GetUserProfileDirectoryW(token, buf, &mut sz) { - 0 if c::GetLastError() != c::ERROR_INSUFFICIENT_BUFFER => 0, + 0 if api::get_last_error().code != c::ERROR_INSUFFICIENT_BUFFER => 0, 0 => sz, _ => sz - 1, // sz includes the null terminator } diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index cd5bf7f15..f4078d359 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -19,8 +19,7 @@ use crate::path::{Path, PathBuf}; use crate::ptr; use crate::sync::Mutex; use crate::sys::args::{self, Arg}; -use crate::sys::c; -use crate::sys::c::NonZeroDWORD; +use crate::sys::c::{self, NonZeroDWORD, EXIT_FAILURE, EXIT_SUCCESS}; use crate::sys::cvt; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; @@ -30,7 +29,7 @@ use crate::sys::stdio; use crate::sys_common::process::{CommandEnv, CommandEnvs}; use crate::sys_common::IntoInner; -use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS}; +use core::ffi::c_void; //////////////////////////////////////////////////////////////////////////////// // Command diff --git a/library/std/src/sys/windows/stack_overflow.rs b/library/std/src/sys/windows/stack_overflow.rs index 0caf0a317..627763da8 100644 --- a/library/std/src/sys/windows/stack_overflow.rs +++ b/library/std/src/sys/windows/stack_overflow.rs @@ -3,6 +3,8 @@ use crate::sys::c; use crate::thread; +use super::api; + pub struct Handler; impl Handler { @@ -10,7 +12,7 @@ impl Handler { // This API isn't available on XP, so don't panic in that case and just // pray it works out ok. if c::SetThreadStackGuarantee(&mut 0x5000) == 0 - && c::GetLastError() as u32 != c::ERROR_CALL_NOT_IMPLEMENTED as u32 + && api::get_last_error().code != c::ERROR_CALL_NOT_IMPLEMENTED { panic!("failed to reserve stack space for exception handling"); } diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index 3fcaaa508..a9ff909aa 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -9,6 +9,7 @@ use crate::str; use crate::sys::c; use crate::sys::cvt; use crate::sys::handle::Handle; +use crate::sys::windows::api; use core::str::utf8_char_width; #[cfg(test)] @@ -369,7 +370,7 @@ fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit<u16>]) -> io::Result<usiz // ReadConsoleW returns success with ERROR_OPERATION_ABORTED for Ctrl-C or Ctrl-Break. // Explicitly check for that case here and try again. - if amount == 0 && unsafe { c::GetLastError() } == c::ERROR_OPERATION_ABORTED { + if amount == 0 && api::get_last_error().code == c::ERROR_OPERATION_ABORTED { continue; } break; diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs index 18cecb656..1fe744935 100644 --- a/library/std/src/sys/windows/thread.rs +++ b/library/std/src/sys/windows/thread.rs @@ -10,8 +10,9 @@ use crate::sys::stack_overflow; use crate::sys_common::FromInner; use crate::time::Duration; -use libc::c_void; +use core::ffi::c_void; +use super::time::WaitableTimer; use super::to_u16s; pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; @@ -87,7 +88,17 @@ impl Thread { } pub fn sleep(dur: Duration) { - unsafe { c::Sleep(super::dur2timeout(dur)) } + fn high_precision_sleep(dur: Duration) -> Result<(), ()> { + let timer = WaitableTimer::high_resolution()?; + timer.set(dur)?; + timer.wait() + } + // Attempt to use high-precision sleep (Windows 10, version 1803+). + // On error fallback to the standard `Sleep` function. + // Also preserves the zero duration behaviour of `Sleep`. + if dur.is_zero() || high_precision_sleep(dur).is_err() { + unsafe { c::Sleep(super::dur2timeout(dur)) } + } } pub fn handle(&self) -> &Handle { diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs index 036d96596..5eee4a966 100644 --- a/library/std/src/sys/windows/thread_local_key.rs +++ b/library/std/src/sys/windows/thread_local_key.rs @@ -16,14 +16,19 @@ static HAS_DTORS: AtomicBool = AtomicBool::new(false); // Using a per-thread list avoids the problems in synchronizing global state. #[thread_local] #[cfg(target_thread_local)] -static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); +static DESTRUCTORS: crate::cell::RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = + crate::cell::RefCell::new(Vec::new()); // Ensure this can never be inlined because otherwise this may break in dylibs. // See #44391. #[inline(never)] #[cfg(target_thread_local)] pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - DESTRUCTORS.push((t, dtor)); + match DESTRUCTORS.try_borrow_mut() { + Ok(mut dtors) => dtors.push((t, dtor)), + Err(_) => rtabort!("global allocator may not use TLS"), + } + HAS_DTORS.store(true, Relaxed); } @@ -37,11 +42,17 @@ unsafe fn run_keyless_dtors() { // the case that this loop always terminates because we provide the // guarantee that a TLS key cannot be set after it is flagged for // destruction. - while let Some((ptr, dtor)) = DESTRUCTORS.pop() { + loop { + // Use a let-else binding to ensure the `RefCell` guard is dropped + // immediately. Otherwise, a panic would occur if a TLS destructor + // tries to access the list. + let Some((ptr, dtor)) = DESTRUCTORS.borrow_mut().pop() else { + break; + }; (dtor)(ptr); } // We're done so free the memory. - DESTRUCTORS = Vec::new(); + DESTRUCTORS.replace(Vec::new()); } type Key = c::DWORD; diff --git a/library/std/src/sys/windows/time.rs b/library/std/src/sys/windows/time.rs index b8209a854..bece48e79 100644 --- a/library/std/src/sys/windows/time.rs +++ b/library/std/src/sys/windows/time.rs @@ -1,11 +1,13 @@ use crate::cmp::Ordering; use crate::fmt; use crate::mem; +use crate::ptr::{null, null_mut}; use crate::sys::c; use crate::sys_common::IntoInner; use crate::time::Duration; use core::hash::{Hash, Hasher}; +use core::ops::Neg; const NANOS_PER_SEC: u64 = 1_000_000_000; const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100; @@ -222,3 +224,39 @@ mod perf_counter { qpc_value } } + +/// A timer you can wait on. +pub(super) struct WaitableTimer { + handle: c::HANDLE, +} +impl WaitableTimer { + /// Create a high-resolution timer. Will fail before Windows 10, version 1803. + pub fn high_resolution() -> Result<Self, ()> { + let handle = unsafe { + c::CreateWaitableTimerExW( + null(), + null(), + c::CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, + c::TIMER_ALL_ACCESS, + ) + }; + if handle != null_mut() { Ok(Self { handle }) } else { Err(()) } + } + pub fn set(&self, duration: Duration) -> Result<(), ()> { + // Convert the Duration to a format similar to FILETIME. + // Negative values are relative times whereas positive values are absolute. + // Therefore we negate the relative duration. + let time = checked_dur2intervals(&duration).ok_or(())?.neg(); + let result = unsafe { c::SetWaitableTimer(self.handle, &time, 0, None, null(), c::FALSE) }; + if result != 0 { Ok(()) } else { Err(()) } + } + pub fn wait(&self) -> Result<(), ()> { + let result = unsafe { c::WaitForSingleObject(self.handle, c::INFINITE) }; + if result != c::WAIT_FAILED { Ok(()) } else { Err(()) } + } +} +impl Drop for WaitableTimer { + fn drop(&mut self) { + unsafe { c::CloseHandle(self.handle) }; + } +} diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 4f5b17dea..8712bd2ec 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -226,9 +226,7 @@ impl TcpStream { init(); let sock = Socket::new(addr, c::SOCK_STREAM)?; - - let (addr, len) = addr.into_inner(); - cvt_r(|| unsafe { c::connect(sock.as_raw(), addr.as_ptr(), len) })?; + sock.connect(addr)?; Ok(TcpStream { inner: sock }) } diff --git a/library/std/src/sys_common/thread_local_dtor.rs b/library/std/src/sys_common/thread_local_dtor.rs index 844946eda..98382fc6a 100644 --- a/library/std/src/sys_common/thread_local_dtor.rs +++ b/library/std/src/sys_common/thread_local_dtor.rs @@ -13,6 +13,7 @@ #![unstable(feature = "thread_local_internals", issue = "none")] #![allow(dead_code)] +use crate::cell::RefCell; use crate::ptr; use crate::sys_common::thread_local_key::StaticKey; @@ -28,17 +29,23 @@ pub unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern "C" fn(*mut // flagged for destruction. static DTORS: StaticKey = StaticKey::new(Some(run_dtors)); - type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>; + // FIXME(joboet): integrate RefCell into pointer to avoid infinite recursion + // when the global allocator tries to register a destructor and just panic + // instead. + type List = RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>>; if DTORS.get().is_null() { - let v: Box<List> = Box::new(Vec::new()); + let v: Box<List> = Box::new(RefCell::new(Vec::new())); DTORS.set(Box::into_raw(v) as *mut u8); } - let list: &mut List = &mut *(DTORS.get() as *mut List); - list.push((t, dtor)); + let list = &*(DTORS.get() as *const List); + match list.try_borrow_mut() { + Ok(mut dtors) => dtors.push((t, dtor)), + Err(_) => rtabort!("global allocator may not use TLS"), + } unsafe extern "C" fn run_dtors(mut ptr: *mut u8) { while !ptr.is_null() { - let list: Box<List> = Box::from_raw(ptr as *mut List); + let list = Box::from_raw(ptr as *mut List).into_inner(); for (ptr, dtor) in list.into_iter() { dtor(ptr); } diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 09994e47f..def94acd4 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -29,9 +29,9 @@ use crate::fmt; /// within a thread, and values that implement [`Drop`] get destructed when a /// thread exits. Some caveats apply, which are explained below. /// -/// A `LocalKey`'s initializer cannot recursively depend on itself, and using -/// a `LocalKey` in this way will cause the initializer to infinitely recurse -/// on the first call to `with`. +/// A `LocalKey`'s initializer cannot recursively depend on itself. Using a +/// `LocalKey` in this way may cause panics, aborts or infinite recursion on +/// the first call to `with`. /// /// # Examples /// diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 7b26068c2..4097eb554 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -545,6 +545,15 @@ impl Builder { scope_data.increment_num_running_threads(); } + let main = Box::new(main); + // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the + // lifetime change is justified. + #[cfg(bootstrap)] + let main = + unsafe { mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(main) }; + #[cfg(not(bootstrap))] + let main = unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + 'static)) }; + Ok(JoinInner { // SAFETY: // @@ -559,14 +568,7 @@ impl Builder { // Similarly, the `sys` implementation must guarantee that no references to the closure // exist after the thread has terminated, which is signaled by `Thread::join` // returning. - native: unsafe { - imp::Thread::new( - stack_size, - mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>( - Box::new(main), - ), - )? - }, + native: unsafe { imp::Thread::new(stack_size, main)? }, thread: my_thread, packet: my_packet, }) diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 005d8c767..91c010ef2 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -111,7 +111,7 @@ pub use core::time::TryFromFloatSecsError; /// |-----------|----------------------------------------------------------------------| /// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | /// | UNIX | [clock_gettime (Monotonic Clock)] | -/// | Darwin | [mach_absolute_time] | +/// | Darwin | [clock_gettime (Monotonic Clock)] | /// | VXWorks | [clock_gettime (Monotonic Clock)] | /// | SOLID | `get_tim` | /// | WASI | [__wasi_clock_time_get (Monotonic Clock)] | @@ -123,7 +123,6 @@ pub use core::time::TryFromFloatSecsError; /// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode /// [__wasi_clock_time_get (Monotonic Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get /// [clock_gettime (Monotonic Clock)]: https://linux.die.net/man/3/clock_gettime -/// [mach_absolute_time]: https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/services/services.html /// /// **Disclaimer:** These system calls might change over time. /// @@ -153,6 +152,7 @@ pub use core::time::TryFromFloatSecsError; /// #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[stable(feature = "time2", since = "1.8.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Instant")] pub struct Instant(time::Instant); /// A measurement of the system clock, useful for talking to @@ -223,7 +223,7 @@ pub struct Instant(time::Instant); /// |-----------|----------------------------------------------------------------------| /// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | /// | UNIX | [clock_gettime (Realtime Clock)] | -/// | Darwin | [gettimeofday] | +/// | Darwin | [clock_gettime (Realtime Clock)] | /// | VXWorks | [clock_gettime (Realtime Clock)] | /// | SOLID | `SOLID_RTC_ReadTime` | /// | WASI | [__wasi_clock_time_get (Realtime Clock)] | @@ -232,7 +232,6 @@ pub struct Instant(time::Instant); /// [currently]: crate::io#platform-specific-behavior /// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time /// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode -/// [gettimeofday]: https://man7.org/linux/man-pages/man2/gettimeofday.2.html /// [clock_gettime (Realtime Clock)]: https://linux.die.net/man/3/clock_gettime /// [__wasi_clock_time_get (Realtime Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get /// [GetSystemTimePreciseAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime |