diff options
Diffstat (limited to 'library/std')
97 files changed, 2723 insertions, 627 deletions
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index f666b1888..fe66788b5 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -30,8 +30,13 @@ rustc-demangle = { version = "0.1.21", features = ['rustc-dep-of-std'] } [target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] miniz_oxide = { version = "0.7.0", optional = true, default-features = false } addr2line = { version = "0.21.0", optional = true, default-features = false } + +[target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies] object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } +[target.'cfg(target_os = "aix")'.dependencies] +object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'xcoff', 'unaligned', 'archive'] } + [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } rand_xorshift = "0.3.0" diff --git a/library/std/build.rs b/library/std/build.rs index ad0a82eab..0f5068b59 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -25,7 +25,6 @@ fn main() { || target.contains("vxworks") || target.contains("wasm32") || target.contains("wasm64") - || target.contains("asmjs") || target.contains("espidf") || target.contains("solid") || target.contains("nintendo-3ds") @@ -35,6 +34,7 @@ fn main() { || target.contains("xous") || target.contains("hurd") || target.contains("uefi") + || target.contains("teeos") // See src/bootstrap/synthetic_targets.rs || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok() { diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index e7110aebd..9638f4919 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -95,8 +95,7 @@ use crate::fmt; use crate::panic::UnwindSafe; use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed}; use crate::sync::LazyLock; -use crate::sys_common::backtrace::{lock, output_filename}; -use crate::vec::Vec; +use crate::sys_common::backtrace::{lock, output_filename, set_image_base}; /// A captured OS thread stack backtrace. /// @@ -327,6 +326,7 @@ impl Backtrace { let _lock = lock(); let mut frames = Vec::new(); let mut actual_start = None; + set_image_base(); unsafe { backtrace_rs::trace_unsynchronized(|frame| { frames.push(BacktraceFrame { diff --git a/library/std/src/backtrace/tests.rs b/library/std/src/backtrace/tests.rs index 73543a3af..174d62813 100644 --- a/library/std/src/backtrace/tests.rs +++ b/library/std/src/backtrace/tests.rs @@ -1,5 +1,5 @@ use super::*; -use crate::panic::{RefUnwindSafe, UnwindSafe}; +use crate::panic::RefUnwindSafe; fn generate_fake_frames() -> Vec<BacktraceFrame> { vec![ diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 4d109285d..39e94902c 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -6,16 +6,13 @@ use self::Entry::*; use hashbrown::hash_map as base; use crate::borrow::Borrow; -use crate::cell::Cell; use crate::collections::TryReserveError; use crate::collections::TryReserveErrorKind; use crate::error::Error; use crate::fmt::{self, Debug}; -#[allow(deprecated)] -use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13}; +use crate::hash::{BuildHasher, Hash, RandomState}; use crate::iter::FusedIterator; use crate::ops::Index; -use crate::sys; /// A [hash map] implemented with quadratic probing and SIMD lookup. /// @@ -274,7 +271,7 @@ impl<K, V, S> HashMap<K, V, S> { /// /// ``` /// use std::collections::HashMap; - /// use std::collections::hash_map::RandomState; + /// use std::hash::RandomState; /// /// let s = RandomState::new(); /// let mut map = HashMap::with_hasher(s); @@ -306,7 +303,7 @@ impl<K, V, S> HashMap<K, V, S> { /// /// ``` /// use std::collections::HashMap; - /// use std::collections::hash_map::RandomState; + /// use std::hash::RandomState; /// /// let s = RandomState::new(); /// let mut map = HashMap::with_capacity_and_hasher(10, s); @@ -717,7 +714,7 @@ impl<K, V, S> HashMap<K, V, S> { /// /// ``` /// use std::collections::HashMap; - /// use std::collections::hash_map::RandomState; + /// use std::hash::RandomState; /// /// let hasher = RandomState::new(); /// let map: HashMap<i32, i32> = HashMap::with_hasher(hasher); @@ -3072,152 +3069,6 @@ where } } -/// `RandomState` is the default state for [`HashMap`] types. -/// -/// A particular instance `RandomState` will create the same instances of -/// [`Hasher`], but the hashers created by two different `RandomState` -/// instances are unlikely to produce the same result for the same values. -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashMap; -/// use std::collections::hash_map::RandomState; -/// -/// let s = RandomState::new(); -/// let mut map = HashMap::with_hasher(s); -/// map.insert(1, 2); -/// ``` -#[derive(Clone)] -#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] -pub struct RandomState { - k0: u64, - k1: u64, -} - -impl RandomState { - /// Constructs a new `RandomState` that is initialized with random keys. - /// - /// # Examples - /// - /// ``` - /// use std::collections::hash_map::RandomState; - /// - /// let s = RandomState::new(); - /// ``` - #[inline] - #[allow(deprecated)] - // rand - #[must_use] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn new() -> RandomState { - // Historically this function did not cache keys from the OS and instead - // simply always called `rand::thread_rng().gen()` twice. In #31356 it - // was discovered, however, that because we re-seed the thread-local RNG - // from the OS periodically that this can cause excessive slowdown when - // many hash maps are created on a thread. To solve this performance - // trap we cache the first set of randomly generated keys per-thread. - // - // Later in #36481 it was discovered that exposing a deterministic - // iteration order allows a form of DOS attack. To counter that we - // increment one of the seeds on every RandomState creation, giving - // every corresponding HashMap a different iteration order. - thread_local!(static KEYS: Cell<(u64, u64)> = { - Cell::new(sys::hashmap_random_keys()) - }); - - KEYS.with(|keys| { - let (k0, k1) = keys.get(); - keys.set((k0.wrapping_add(1), k1)); - RandomState { k0, k1 } - }) - } -} - -#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] -impl BuildHasher for RandomState { - type Hasher = DefaultHasher; - #[inline] - #[allow(deprecated)] - fn build_hasher(&self) -> DefaultHasher { - DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1)) - } -} - -/// The default [`Hasher`] used by [`RandomState`]. -/// -/// The internal algorithm is not specified, and so it and its hashes should -/// not be relied upon over releases. -#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] -#[allow(deprecated)] -#[derive(Clone, Debug)] -pub struct DefaultHasher(SipHasher13); - -impl DefaultHasher { - /// Creates a new `DefaultHasher`. - /// - /// This hasher is not guaranteed to be the same as all other - /// `DefaultHasher` instances, but is the same as all other `DefaultHasher` - /// instances created through `new` or `default`. - #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] - #[inline] - #[allow(deprecated)] - #[rustc_const_unstable(feature = "const_hash", issue = "104061")] - #[must_use] - pub const fn new() -> DefaultHasher { - DefaultHasher(SipHasher13::new_with_keys(0, 0)) - } -} - -#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] -impl Default for DefaultHasher { - /// Creates a new `DefaultHasher` using [`new`]. - /// See its documentation for more. - /// - /// [`new`]: DefaultHasher::new - #[inline] - fn default() -> DefaultHasher { - DefaultHasher::new() - } -} - -#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] -impl Hasher for DefaultHasher { - // The underlying `SipHasher13` doesn't override the other - // `write_*` methods, so it's ok not to forward them here. - - #[inline] - fn write(&mut self, msg: &[u8]) { - self.0.write(msg) - } - - #[inline] - fn write_str(&mut self, s: &str) { - self.0.write_str(s); - } - - #[inline] - fn finish(&self) -> u64 { - self.0.finish() - } -} - -#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] -impl Default for RandomState { - /// Constructs a new `RandomState`. - #[inline] - fn default() -> RandomState { - RandomState::new() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for RandomState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RandomState").finish_non_exhaustive() - } -} - #[inline] fn map_entry<'a, K: 'a, V: 'a>(raw: base::RustcEntry<'a, K, V>) -> Entry<'a, K, V> { match raw { diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index 91a3776e7..8585376ab 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -1,8 +1,8 @@ use super::Entry::{Occupied, Vacant}; use super::HashMap; -use super::RandomState; use crate::assert_matches::assert_matches; use crate::cell::RefCell; +use crate::hash::RandomState; use crate::test_helpers::test_rng; use rand::Rng; use realstd::collections::TryReserveErrorKind::*; diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 6a87f6e5f..8bc596082 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -6,11 +6,11 @@ use hashbrown::hash_set as base; use crate::borrow::Borrow; use crate::collections::TryReserveError; use crate::fmt; -use crate::hash::{BuildHasher, Hash}; +use crate::hash::{BuildHasher, Hash, RandomState}; use crate::iter::{Chain, FusedIterator}; use crate::ops::{BitAnd, BitOr, BitXor, Sub}; -use super::map::{map_try_reserve_error, RandomState}; +use super::map::map_try_reserve_error; /// A [hash set] implemented as a `HashMap` where the value is `()`. /// @@ -361,7 +361,7 @@ impl<T, S> HashSet<T, S> { /// /// ``` /// use std::collections::HashSet; - /// use std::collections::hash_map::RandomState; + /// use std::hash::RandomState; /// /// let s = RandomState::new(); /// let mut set = HashSet::with_hasher(s); @@ -393,7 +393,7 @@ impl<T, S> HashSet<T, S> { /// /// ``` /// use std::collections::HashSet; - /// use std::collections::hash_map::RandomState; + /// use std::hash::RandomState; /// /// let s = RandomState::new(); /// let mut set = HashSet::with_capacity_and_hasher(10, s); @@ -411,7 +411,7 @@ impl<T, S> HashSet<T, S> { /// /// ``` /// use std::collections::HashSet; - /// use std::collections::hash_map::RandomState; + /// use std::hash::RandomState; /// /// let hasher = RandomState::new(); /// let set: HashSet<i32> = HashSet::with_hasher(hasher); diff --git a/library/std/src/collections/hash/set/tests.rs b/library/std/src/collections/hash/set/tests.rs index e0cd80b44..208f61e75 100644 --- a/library/std/src/collections/hash/set/tests.rs +++ b/library/std/src/collections/hash/set/tests.rs @@ -1,6 +1,6 @@ -use super::super::map::RandomState; use super::HashSet; +use crate::hash::RandomState; use crate::panic::{catch_unwind, AssertUnwindSafe}; use crate::sync::atomic::{AtomicU32, Ordering}; use crate::sync::Arc; diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs index 42f738acb..1389d24a8 100644 --- a/library/std/src/collections/mod.rs +++ b/library/std/src/collections/mod.rs @@ -439,6 +439,11 @@ pub mod hash_map { //! A hash map implemented with quadratic probing and SIMD lookup. #[stable(feature = "rust1", since = "1.0.0")] pub use super::hash::map::*; + + #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] + pub use crate::hash::random::DefaultHasher; + #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] + pub use crate::hash::random::RandomState; } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/env.rs b/library/std/src/env.rs index f67f6034d..30ac05123 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -313,17 +313,32 @@ impl Error for VarError { /// Sets the environment variable `key` to the value `value` for the currently running /// process. /// -/// Note that while concurrent access to environment variables is safe in Rust, -/// some platforms only expose inherently unsafe non-threadsafe APIs for -/// inspecting the environment. As a result, extra care needs to be taken when -/// auditing calls to unsafe external FFI functions to ensure that any external -/// environment accesses are properly synchronized with accesses in Rust. +/// # Safety +/// +/// Even though this function is currently not marked as `unsafe`, it needs to +/// be because invoking it can cause undefined behaviour. The function will be +/// marked `unsafe` in a future version of Rust. This is tracked in +/// [rust#27970](https://github.com/rust-lang/rust/issues/27970). +/// +/// This function is safe to call in a single-threaded program. +/// +/// In multi-threaded programs, you must ensure that are no other threads +/// concurrently writing or *reading*(!) from the environment through functions +/// other than the ones in this module. You are responsible for figuring out +/// how to achieve this, but we strongly suggest not using `set_var` or +/// `remove_var` in multi-threaded programs at all. +/// +/// Most C libraries, including libc itself do not advertise which functions +/// read from the environment. Even functions from the Rust standard library do +/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`]. /// /// Discussion of this unsafety on Unix may be found in: /// /// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188) /// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) /// +/// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs +/// /// # Panics /// /// This function may panic if `key` is empty, contains an ASCII equals sign `'='` @@ -351,17 +366,32 @@ fn _set_var(key: &OsStr, value: &OsStr) { /// Removes an environment variable from the environment of the currently running process. /// -/// Note that while concurrent access to environment variables is safe in Rust, -/// some platforms only expose inherently unsafe non-threadsafe APIs for -/// inspecting the environment. As a result extra care needs to be taken when -/// auditing calls to unsafe external FFI functions to ensure that any external -/// environment accesses are properly synchronized with accesses in Rust. +/// # Safety +/// +/// Even though this function is currently not marked as `unsafe`, it needs to +/// be because invoking it can cause undefined behaviour. The function will be +/// marked `unsafe` in a future version of Rust. This is tracked in +/// [rust#27970](https://github.com/rust-lang/rust/issues/27970). +/// +/// This function is safe to call in a single-threaded program. +/// +/// In multi-threaded programs, you must ensure that are no other threads +/// concurrently writing or *reading*(!) from the environment through functions +/// other than the ones in this module. You are responsible for figuring out +/// how to achieve this, but we strongly suggest not using `set_var` or +/// `remove_var` in multi-threaded programs at all. +/// +/// Most C libraries, including libc itself do not advertise which functions +/// read from the environment. Even functions from the Rust standard library do +/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`]. /// /// Discussion of this unsafety on Unix may be found in: /// /// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188) /// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) /// +/// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs +/// /// # Panics /// /// This function may panic if `key` is empty, contains an ASCII equals sign diff --git a/library/std/src/env/tests.rs b/library/std/src/env/tests.rs index 558692295..fc7aee297 100644 --- a/library/std/src/env/tests.rs +++ b/library/std/src/env/tests.rs @@ -1,7 +1,5 @@ use super::*; -use crate::path::Path; - #[test] #[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)] fn test_self_exe_path() { diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 375ff2d24..b240e4e2c 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -12,14 +12,6 @@ pub use core::error::Error; #[unstable(feature = "error_generic_member_access", issue = "99301")] pub use core::error::{request_ref, request_value, Request}; -mod private { - // This is a hack to prevent `type_id` from being overridden by `Error` - // implementations, since that can enable unsound downcasting. - #[unstable(feature = "error_type_id", issue = "60784")] - #[derive(Debug)] - pub struct Internal; -} - /// An error reporter that prints an error and its sources. /// /// Report also exposes configuration options for formatting the error sources, either entirely on a diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index fa9d48771..819731821 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -6,9 +6,10 @@ use crate::cmp; use crate::collections::TryReserveError; use crate::fmt; use crate::hash::{Hash, Hasher}; -use crate::ops; +use crate::ops::{self, Range}; use crate::rc::Rc; -use crate::str::FromStr; +use crate::slice; +use crate::str::{from_utf8 as str_from_utf8, FromStr}; use crate::sync::Arc; use crate::sys::os_str::{Buf, Slice}; @@ -963,6 +964,83 @@ impl OsStr { self.inner.as_encoded_bytes() } + /// Takes a substring based on a range that corresponds to the return value of + /// [`OsStr::as_encoded_bytes`]. + /// + /// The range's start and end must lie on valid `OsStr` boundaries. + /// A valid `OsStr` boundary is one of: + /// - The start of the string + /// - The end of the string + /// - Immediately before a valid non-empty UTF-8 substring + /// - Immediately after a valid non-empty UTF-8 substring + /// + /// # Panics + /// + /// Panics if `range` does not lie on valid `OsStr` boundaries or if it + /// exceeds the end of the string. + /// + /// # Example + /// + /// ``` + /// #![feature(os_str_slice)] + /// + /// use std::ffi::OsStr; + /// + /// let os_str = OsStr::new("foo=bar"); + /// let bytes = os_str.as_encoded_bytes(); + /// if let Some(index) = bytes.iter().position(|b| *b == b'=') { + /// let key = os_str.slice_encoded_bytes(..index); + /// let value = os_str.slice_encoded_bytes(index + 1..); + /// assert_eq!(key, "foo"); + /// assert_eq!(value, "bar"); + /// } + /// ``` + #[unstable(feature = "os_str_slice", issue = "118485")] + pub fn slice_encoded_bytes<R: ops::RangeBounds<usize>>(&self, range: R) -> &Self { + #[track_caller] + fn check_valid_boundary(bytes: &[u8], index: usize) { + if index == 0 || index == bytes.len() { + return; + } + + // Fast path + if bytes[index - 1].is_ascii() || bytes[index].is_ascii() { + return; + } + + let (before, after) = bytes.split_at(index); + + // UTF-8 takes at most 4 bytes per codepoint, so we don't + // need to check more than that. + let after = after.get(..4).unwrap_or(after); + match str_from_utf8(after) { + Ok(_) => return, + Err(err) if err.valid_up_to() != 0 => return, + Err(_) => (), + } + + for len in 2..=4.min(index) { + let before = &before[index - len..]; + if str_from_utf8(before).is_ok() { + return; + } + } + + panic!("byte index {index} is not an OsStr boundary"); + } + + let encoded_bytes = self.as_encoded_bytes(); + let Range { start, end } = slice::range(range, ..encoded_bytes.len()); + check_valid_boundary(encoded_bytes, start); + check_valid_boundary(encoded_bytes, end); + + // SAFETY: `slice::range` ensures that `start` and `end` are valid + let slice = unsafe { encoded_bytes.get_unchecked(start..end) }; + + // SAFETY: `slice` comes from `self` and we validated the boundaries + unsafe { Self::from_encoded_bytes_unchecked(slice) } + } + /// Converts this string to its ASCII lower case equivalent in-place. /// /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index d7926749a..60cde376d 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -1,8 +1,4 @@ use super::*; -use crate::sys_common::{AsInner, IntoInner}; - -use crate::rc::Rc; -use crate::sync::Arc; #[test] fn test_os_string_with_capacity() { @@ -177,3 +173,53 @@ fn into_rc() { assert_eq!(&*rc2, os_str); assert_eq!(&*arc2, os_str); } + +#[test] +fn slice_encoded_bytes() { + let os_str = OsStr::new("123θგ🦀"); + // ASCII + let digits = os_str.slice_encoded_bytes(..3); + assert_eq!(digits, "123"); + let three = os_str.slice_encoded_bytes(2..3); + assert_eq!(three, "3"); + // 2-byte UTF-8 + let theta = os_str.slice_encoded_bytes(3..5); + assert_eq!(theta, "θ"); + // 3-byte UTF-8 + let gani = os_str.slice_encoded_bytes(5..8); + assert_eq!(gani, "გ"); + // 4-byte UTF-8 + let crab = os_str.slice_encoded_bytes(8..); + assert_eq!(crab, "🦀"); +} + +#[test] +#[should_panic(expected = "byte index 2 is not an OsStr boundary")] +fn slice_mid_char() { + let crab = OsStr::new("🦀"); + let _ = crab.slice_encoded_bytes(..2); +} + +#[cfg(windows)] +#[test] +#[should_panic(expected = "byte index 3 is not an OsStr boundary")] +fn slice_between_surrogates() { + use crate::os::windows::ffi::OsStringExt; + + let os_string = OsString::from_wide(&[0xD800, 0xD800]); + assert_eq!(os_string.as_encoded_bytes(), &[0xED, 0xA0, 0x80, 0xED, 0xA0, 0x80]); + let _ = os_string.slice_encoded_bytes(..3); +} + +#[cfg(windows)] +#[test] +fn slice_surrogate_edge() { + use crate::os::windows::ffi::OsStringExt; + + let os_string = OsString::from_wide(&[0xD800]); + let mut with_crab = os_string.clone(); + with_crab.push("🦀"); + + assert_eq!(with_crab.slice_encoded_bytes(..3), os_string); + assert_eq!(with_crab.slice_encoded_bytes(3..), "🦀"); +} diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 547a7b705..12afdef26 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -22,7 +22,7 @@ use crate::os::unix::fs::symlink as symlink_file; #[cfg(unix)] use crate::os::unix::fs::symlink as symlink_junction; #[cfg(windows)] -use crate::os::windows::fs::{symlink_dir, symlink_file}; +use crate::os::windows::fs::{symlink_dir, symlink_file, OpenOptionsExt}; #[cfg(windows)] use crate::sys::fs::symlink_junction; #[cfg(target_os = "macos")] @@ -74,7 +74,7 @@ macro_rules! error_contains { // tests most of the time, but at least we do if the user has the right // permissions. pub fn got_symlink_permission(tmpdir: &TempDir) -> bool { - if cfg!(unix) { + if cfg!(not(windows)) || env::var_os("CI").is_some() { return true; } let link = tmpdir.join("some_hopefully_unique_link_name"); @@ -1793,3 +1793,28 @@ fn windows_unix_socket_exists() { assert_eq!(socket_path.try_exists().unwrap(), true); assert_eq!(socket_path.metadata().is_ok(), true); } + +#[cfg(windows)] +#[test] +fn test_hidden_file_truncation() { + // Make sure that File::create works on an existing hidden file. See #115745. + let tmpdir = tmpdir(); + let path = tmpdir.join("hidden_file.txt"); + + // Create a hidden file. + const FILE_ATTRIBUTE_HIDDEN: u32 = 2; + let mut file = OpenOptions::new() + .write(true) + .create_new(true) + .attributes(FILE_ATTRIBUTE_HIDDEN) + .open(&path) + .unwrap(); + file.write("hidden world!".as_bytes()).unwrap(); + file.flush().unwrap(); + drop(file); + + // Create a new file by truncating the existing one. + let file = File::create(&path).unwrap(); + let metadata = file.metadata().unwrap(); + assert_eq!(metadata.len(), 0); +} diff --git a/library/std/src/hash/mod.rs b/library/std/src/hash/mod.rs new file mode 100644 index 000000000..e5ef9e335 --- /dev/null +++ b/library/std/src/hash/mod.rs @@ -0,0 +1,91 @@ +//! Generic hashing support. +//! +//! This module provides a generic way to compute the [hash] of a value. +//! Hashes are most commonly used with [`HashMap`] and [`HashSet`]. +//! +//! [hash]: https://en.wikipedia.org/wiki/Hash_function +//! [`HashMap`]: ../../std/collections/struct.HashMap.html +//! [`HashSet`]: ../../std/collections/struct.HashSet.html +//! +//! The simplest way to make a type hashable is to use `#[derive(Hash)]`: +//! +//! # Examples +//! +//! ```rust +//! use std::hash::{DefaultHasher, Hash, Hasher}; +//! +//! #[derive(Hash)] +//! struct Person { +//! id: u32, +//! name: String, +//! phone: u64, +//! } +//! +//! let person1 = Person { +//! id: 5, +//! name: "Janet".to_string(), +//! phone: 555_666_7777, +//! }; +//! let person2 = Person { +//! id: 5, +//! name: "Bob".to_string(), +//! phone: 555_666_7777, +//! }; +//! +//! assert!(calculate_hash(&person1) != calculate_hash(&person2)); +//! +//! fn calculate_hash<T: Hash>(t: &T) -> u64 { +//! let mut s = DefaultHasher::new(); +//! t.hash(&mut s); +//! s.finish() +//! } +//! ``` +//! +//! If you need more control over how a value is hashed, you need to implement +//! the [`Hash`] trait: +//! +//! ```rust +//! use std::hash::{DefaultHasher, Hash, Hasher}; +//! +//! struct Person { +//! id: u32, +//! # #[allow(dead_code)] +//! name: String, +//! phone: u64, +//! } +//! +//! impl Hash for Person { +//! fn hash<H: Hasher>(&self, state: &mut H) { +//! self.id.hash(state); +//! self.phone.hash(state); +//! } +//! } +//! +//! let person1 = Person { +//! id: 5, +//! name: "Janet".to_string(), +//! phone: 555_666_7777, +//! }; +//! let person2 = Person { +//! id: 5, +//! name: "Bob".to_string(), +//! phone: 555_666_7777, +//! }; +//! +//! assert_eq!(calculate_hash(&person1), calculate_hash(&person2)); +//! +//! fn calculate_hash<T: Hash>(t: &T) -> u64 { +//! let mut s = DefaultHasher::new(); +//! t.hash(&mut s); +//! s.finish() +//! } +//! ``` +#![stable(feature = "rust1", since = "1.0.0")] + +pub(crate) mod random; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::hash::*; + +#[stable(feature = "std_hash_exports", since = "1.76.0")] +pub use self::random::{DefaultHasher, RandomState}; diff --git a/library/std/src/hash/random.rs b/library/std/src/hash/random.rs new file mode 100644 index 000000000..a1ccbb253 --- /dev/null +++ b/library/std/src/hash/random.rs @@ -0,0 +1,161 @@ +//! This module exists to isolate [`RandomState`] and [`DefaultHasher`] outside of the +//! [`collections`] module without actually publicly exporting them, so that parts of that +//! implementation can more easily be moved to the [`alloc`] crate. +//! +//! Although its items are public and contain stability attributes, they can't actually be accessed +//! outside this crate. +//! +//! [`collections`]: crate::collections +#[allow(deprecated)] +use super::{BuildHasher, Hasher, SipHasher13}; +use crate::cell::Cell; +use crate::fmt; +use crate::sys; + +/// `RandomState` is the default state for [`HashMap`] types. +/// +/// A particular instance `RandomState` will create the same instances of +/// [`Hasher`], but the hashers created by two different `RandomState` +/// instances are unlikely to produce the same result for the same values. +/// +/// [`HashMap`]: crate::collections::HashMap +/// +/// # Examples +/// +/// ``` +/// use std::collections::HashMap; +/// use std::hash::RandomState; +/// +/// let s = RandomState::new(); +/// let mut map = HashMap::with_hasher(s); +/// map.insert(1, 2); +/// ``` +#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] +#[derive(Clone)] +pub struct RandomState { + k0: u64, + k1: u64, +} + +impl RandomState { + /// Constructs a new `RandomState` that is initialized with random keys. + /// + /// # Examples + /// + /// ``` + /// use std::hash::RandomState; + /// + /// let s = RandomState::new(); + /// ``` + #[inline] + #[allow(deprecated)] + // rand + #[must_use] + #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] + pub fn new() -> RandomState { + // Historically this function did not cache keys from the OS and instead + // simply always called `rand::thread_rng().gen()` twice. In #31356 it + // was discovered, however, that because we re-seed the thread-local RNG + // from the OS periodically that this can cause excessive slowdown when + // many hash maps are created on a thread. To solve this performance + // trap we cache the first set of randomly generated keys per-thread. + // + // Later in #36481 it was discovered that exposing a deterministic + // iteration order allows a form of DOS attack. To counter that we + // increment one of the seeds on every RandomState creation, giving + // every corresponding HashMap a different iteration order. + thread_local!(static KEYS: Cell<(u64, u64)> = { + Cell::new(sys::hashmap_random_keys()) + }); + + KEYS.with(|keys| { + let (k0, k1) = keys.get(); + keys.set((k0.wrapping_add(1), k1)); + RandomState { k0, k1 } + }) + } +} + +#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] +impl BuildHasher for RandomState { + type Hasher = DefaultHasher; + #[inline] + #[allow(deprecated)] + fn build_hasher(&self) -> DefaultHasher { + DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1)) + } +} + +/// The default [`Hasher`] used by [`RandomState`]. +/// +/// The internal algorithm is not specified, and so it and its hashes should +/// not be relied upon over releases. +#[allow(deprecated)] +#[derive(Clone, Debug)] +#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] +pub struct DefaultHasher(SipHasher13); + +impl DefaultHasher { + /// Creates a new `DefaultHasher`. + /// + /// This hasher is not guaranteed to be the same as all other + /// `DefaultHasher` instances, but is the same as all other `DefaultHasher` + /// instances created through `new` or `default`. + #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] + #[inline] + #[allow(deprecated)] + #[rustc_const_unstable(feature = "const_hash", issue = "104061")] + #[must_use] + pub const fn new() -> DefaultHasher { + DefaultHasher(SipHasher13::new_with_keys(0, 0)) + } +} + +#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] +impl Default for DefaultHasher { + /// Creates a new `DefaultHasher` using [`new`]. + /// See its documentation for more. + /// + /// [`new`]: DefaultHasher::new + #[inline] + fn default() -> DefaultHasher { + DefaultHasher::new() + } +} + +#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] +impl Hasher for DefaultHasher { + // The underlying `SipHasher13` doesn't override the other + // `write_*` methods, so it's ok not to forward them here. + + #[inline] + fn write(&mut self, msg: &[u8]) { + self.0.write(msg) + } + + #[inline] + fn write_str(&mut self, s: &str) { + self.0.write_str(s); + } + + #[inline] + fn finish(&self) -> u64 { + self.0.finish() + } +} + +#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] +impl Default for RandomState { + /// Constructs a new `RandomState`. + #[inline] + fn default() -> RandomState { + RandomState::new() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for RandomState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RandomState").finish_non_exhaustive() + } +} diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 55aafc3db..6c7494a6a 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -507,6 +507,16 @@ impl<R: ?Sized + Seek> Seek for BufReader<R> { ) }) } + + /// Seeks relative to the current position. + /// + /// If the new position lies within the buffer, the buffer will not be + /// flushed, allowing for more efficient seeks. This method does not return + /// the location of the underlying reader, so the caller must track this + /// information themselves if it is required. + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + self.seek_relative(offset) + } } impl<T: ?Sized> SizeHint for BufReader<T> { diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index 4d51a719f..d49866345 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,7 +1,6 @@ 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; @@ -256,79 +255,17 @@ impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> { } } -impl<A: Allocator> BufferedWriterSpec for Vec<u8, A> { +impl BufferedWriterSpec for Vec<u8> { 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), - } - } - } + reader.read_to_end(self).map(|bytes| u64::try_from(bytes).expect("usize overflowed u64")) } } -fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>( +pub fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>( reader: &mut R, writer: &mut W, ) -> Result<u64> { diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index af137eaf8..a1f909a3c 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -82,13 +82,16 @@ 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 cap = DEFAULT_BUF_SIZE * 10; + let mut source = ShortReader { cap, observed_buffer: 0, read_size: DEFAULT_BUF_SIZE }; let mut sink = Vec::new(); - assert_eq!(cap as u64, io::copy(&mut source, &mut sink).unwrap()); + let copied = io::copy(&mut source, &mut sink).unwrap(); + assert_eq!(cap as u64, copied); + assert_eq!(sink.len() as u64, copied); assert!( source.observed_buffer > DEFAULT_BUF_SIZE, - "expected a large buffer to be provided to the reader" + "expected a large buffer to be provided to the reader, got {}", + source.observed_buffer ); } diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index 6e7366b36..db1756597 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -103,7 +103,6 @@ //! the time. use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; -use alloc::boxed::Box; use core::marker::PhantomData; use core::mem::{align_of, size_of}; use core::ptr::{self, NonNull}; diff --git a/library/std/src/io/error/repr_unpacked.rs b/library/std/src/io/error/repr_unpacked.rs index 093fde337..dc8a95577 100644 --- a/library/std/src/io/error/repr_unpacked.rs +++ b/library/std/src/io/error/repr_unpacked.rs @@ -3,7 +3,6 @@ //! would have no benefit. use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; -use alloc::boxed::Box; type Inner = ErrorData<Box<Custom>>; diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 7d70a0bac..e3aa97374 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -397,12 +397,16 @@ where } } -// This uses an adaptive system to extend the vector when it fills. We want to -// avoid paying to allocate and zero a huge chunk of memory if the reader only -// has 4 bytes while still making large reads if the reader does have a ton -// of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every -// time is 4,500 times (!) slower than a default reservation size of 32 if the -// reader has a very small amount of data to return. +// Here we must serve many masters with conflicting goals: +// +// - avoid allocating unless necessary +// - avoid overallocating if we know the exact size (#89165) +// - avoid passing large buffers to readers that always initialize the free capacity if they perform short reads (#23815, #23820) +// - pass large buffers to readers that do not initialize the spare capacity. this can amortize per-call overheads +// - and finally pass not-too-small and not-too-large buffers to Windows read APIs because they manage to suffer from both problems +// at the same time, i.e. small reads suffer from syscall overhead, all reads incur initialization cost +// proportional to buffer size (#110650) +// pub(crate) fn default_read_to_end<R: Read + ?Sized>( r: &mut R, buf: &mut Vec<u8>, @@ -412,20 +416,58 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>( let start_cap = buf.capacity(); // Optionally limit the maximum bytes read on each iteration. // This adds an arbitrary fiddle factor to allow for more data than we expect. - let max_read_size = - size_hint.and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE)); + let mut max_read_size = size_hint + .and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE)) + .unwrap_or(DEFAULT_BUF_SIZE); let mut initialized = 0; // Extra initialized bytes from previous loop iteration + + const PROBE_SIZE: usize = 32; + + fn small_probe_read<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<usize> { + let mut probe = [0u8; PROBE_SIZE]; + + loop { + match r.read(&mut probe) { + Ok(n) => { + buf.extend_from_slice(&probe[..n]); + return Ok(n); + } + Err(ref e) if e.is_interrupted() => continue, + Err(e) => return Err(e), + } + } + } + + // avoid inflating empty/small vecs before we have determined that there's anything to read + if (size_hint.is_none() || size_hint == Some(0)) && buf.capacity() - buf.len() < PROBE_SIZE { + let read = small_probe_read(r, buf)?; + + if read == 0 { + return Ok(0); + } + } + loop { + if buf.len() == buf.capacity() && buf.capacity() == start_cap { + // The buffer might be an exact fit. Let's read into a probe buffer + // and see if it returns `Ok(0)`. If so, we've avoided an + // unnecessary doubling of the capacity. But if not, append the + // probe buffer to the primary buffer and let its capacity grow. + let read = small_probe_read(r, buf)?; + + if read == 0 { + return Ok(buf.len() - start_len); + } + } + if buf.len() == buf.capacity() { - buf.reserve(32); // buf is full, need more space + buf.reserve(PROBE_SIZE); // buf is full, need more space } let mut spare = buf.spare_capacity_mut(); - if let Some(size) = max_read_size { - let len = cmp::min(spare.len(), size); - spare = &mut spare[..len] - } + let buf_len = cmp::min(spare.len(), max_read_size); + spare = &mut spare[..buf_len]; let mut read_buf: BorrowedBuf<'_> = spare.into(); // SAFETY: These bytes were initialized but not filled in the previous loop @@ -434,42 +476,44 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>( } let mut cursor = read_buf.unfilled(); - match r.read_buf(cursor.reborrow()) { - Ok(()) => {} - Err(e) if e.is_interrupted() => continue, - Err(e) => return Err(e), + loop { + match r.read_buf(cursor.reborrow()) { + Ok(()) => break, + Err(e) if e.is_interrupted() => continue, + Err(e) => return Err(e), + } } - if cursor.written() == 0 { + let unfilled_but_initialized = cursor.init_ref().len(); + let bytes_read = cursor.written(); + let was_fully_initialized = read_buf.init_len() == buf_len; + + if bytes_read == 0 { return Ok(buf.len() - start_len); } // store how much was initialized but not filled - initialized = cursor.init_ref().len(); + initialized = unfilled_but_initialized; // SAFETY: BorrowedBuf's invariants mean this much memory is initialized. unsafe { - let new_len = read_buf.filled().len() + buf.len(); + let new_len = bytes_read + buf.len(); buf.set_len(new_len); } - if buf.len() == buf.capacity() && buf.capacity() == start_cap { - // The buffer might be an exact fit. Let's read into a probe buffer - // and see if it returns `Ok(0)`. If so, we've avoided an - // unnecessary doubling of the capacity. But if not, append the - // probe buffer to the primary buffer and let its capacity grow. - let mut probe = [0u8; 32]; - - loop { - match r.read(&mut probe) { - Ok(0) => return Ok(buf.len() - start_len), - Ok(n) => { - buf.extend_from_slice(&probe[..n]); - break; - } - Err(ref e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - } + // Use heuristics to determine the max read size if no initial size hint was provided + if size_hint.is_none() { + // The reader is returning short reads but it doesn't call ensure_init(). + // In that case we no longer need to restrict read sizes to avoid + // initialization costs. + if !was_fully_initialized { + max_read_size = usize::MAX; + } + + // we have passed a larger buffer than previously and the + // reader still hasn't returned a short read + if buf_len >= max_read_size && bytes_read == buf_len { + max_read_size = max_read_size.saturating_mul(2); } } } @@ -556,6 +600,10 @@ where /// therefore, using something that implements [`BufRead`], such as /// [`BufReader`], will be more efficient. /// +/// Repeated calls to the reader use the same cursor, so for example +/// calling `read_to_end` twice on a [`File`] will only return the file's +/// contents once. It's recommended to first call `rewind()` in that case. +/// /// # Examples /// /// [`File`]s implement `Read`: @@ -1957,6 +2005,36 @@ pub trait Seek { fn stream_position(&mut self) -> Result<u64> { self.seek(SeekFrom::Current(0)) } + + /// Seeks relative to the current position. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but + /// doesn't return the new position which can allow some implementations + /// such as [`BufReader`] to perform more efficient seeks. + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_seek_relative)] + /// use std::{ + /// io::{self, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// f.seek_relative(10)?; + /// assert_eq!(f.stream_position()?, 10); + /// Ok(()) + /// } + /// ``` + /// + /// [`BufReader`]: crate::io::BufReader + #[unstable(feature = "seek_seek_relative", issue = "117374")] + fn seek_relative(&mut self, offset: i64) -> Result<()> { + self.seek(SeekFrom::Current(offset))?; + Ok(()) + } } /// Enumeration of possible methods to seek within an I/O object. @@ -2014,6 +2092,28 @@ fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>) -> R } } +fn skip_until<R: BufRead + ?Sized>(r: &mut R, delim: u8) -> Result<usize> { + let mut read = 0; + loop { + let (done, used) = { + let available = match r.fill_buf() { + Ok(n) => n, + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + }; + match memchr::memchr(delim, available) { + Some(i) => (true, i + 1), + None => (false, available.len()), + } + }; + r.consume(used); + read += used; + if done || used == 0 { + return Ok(read); + } + } +} + /// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it /// to perform extra ways of reading. /// @@ -2217,6 +2317,68 @@ pub trait BufRead: Read { read_until(self, byte, buf) } + /// Skip all bytes until the delimiter `byte` or EOF is reached. + /// + /// This function will read (and discard) bytes from the underlying stream until the + /// delimiter or EOF is found. + /// + /// If successful, this function will return the total number of bytes read, + /// including the delimiter byte. + /// + /// This is useful for efficiently skipping data such as NUL-terminated strings + /// in binary file formats without buffering. + /// + /// This function is blocking and should be used carefully: it is possible for + /// an attacker to continuously send bytes without ever sending the delimiter + /// or EOF. + /// + /// # Errors + /// + /// This function will ignore all instances of [`ErrorKind::Interrupted`] and + /// will otherwise return any errors returned by [`fill_buf`]. + /// + /// If an I/O error is encountered then all bytes read so far will be + /// present in `buf` and its length will have been adjusted appropriately. + /// + /// [`fill_buf`]: BufRead::fill_buf + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to read some NUL-terminated information + /// about Ferris from a binary string, skipping the fun fact: + /// + /// ``` + /// #![feature(bufread_skip_until)] + /// + /// use std::io::{self, BufRead}; + /// + /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0"); + /// + /// // read name + /// let mut name = Vec::new(); + /// let num_bytes = cursor.read_until(b'\0', &mut name) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 7); + /// assert_eq!(name, b"Ferris\0"); + /// + /// // skip fun fact + /// let num_bytes = cursor.skip_until(b'\0') + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 30); + /// + /// // read animal type + /// let mut animal = Vec::new(); + /// let num_bytes = cursor.read_until(b'\0', &mut animal) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 11); + /// assert_eq!(animal, b"Crustacean\0"); + /// ``` + #[unstable(feature = "bufread_skip_until", issue = "111735")] + fn skip_until(&mut self, byte: u8) -> Result<usize> { + skip_until(self, byte) + } + /// Read all bytes until a newline (the `0xA` byte) is reached, and append /// them to the provided `String` buffer. /// diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 6d30f5e6c..bda5b721a 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -26,6 +26,36 @@ fn read_until() { } #[test] +fn skip_until() { + let bytes: &[u8] = b"read\0ignore\0read\0ignore\0read\0ignore\0"; + let mut reader = BufReader::new(bytes); + + // read from the bytes, alternating between + // consuming `read\0`s and skipping `ignore\0`s + loop { + // consume `read\0` + let mut out = Vec::new(); + let read = reader.read_until(0, &mut out).unwrap(); + if read == 0 { + // eof + break; + } else { + assert_eq!(out, b"read\0"); + assert_eq!(read, b"read\0".len()); + } + + // skip past `ignore\0` + let skipped = reader.skip_until(0).unwrap(); + assert_eq!(skipped, b"ignore\0".len()); + } + + // ensure we are at the end of the byte slice and that we can skip no further + // also ensure skip_until matches the behavior of read_until at EOF + let skipped = reader.skip_until(0).unwrap(); + assert_eq!(skipped, 0); +} + +#[test] fn split() { let buf = Cursor::new(&b"12"[..]); let mut s = buf.split(b'3'); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 425890122..34b381b6c 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -227,7 +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(rust_logo)] #![doc(cfg_hide( not(test), not(any(test, bootstrap)), @@ -308,6 +308,7 @@ // // Library features (core): // tidy-alphabetical-start +#![feature(c_str_literals)] #![feature(char_internals)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] @@ -317,6 +318,7 @@ #![feature(error_iter)] #![feature(exact_size_is_empty)] #![feature(exclusive_wrapper)] +#![feature(exposed_provenance)] #![feature(extend_one)] #![feature(float_gamma)] #![feature(float_minimum_maximum)] @@ -340,6 +342,7 @@ #![feature(round_ties_even)] #![feature(slice_internals)] #![feature(slice_ptr_get)] +#![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] #![feature(strict_provenance)] @@ -494,8 +497,6 @@ pub use core::convert; pub use core::default; #[stable(feature = "futures_api", since = "1.36.0")] pub use core::future; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::hash; #[stable(feature = "core_hint", since = "1.27.0")] pub use core::hint; #[stable(feature = "i128", since = "1.26.0")] @@ -565,6 +566,7 @@ pub mod env; pub mod error; pub mod ffi; pub mod fs; +pub mod hash; pub mod io; pub mod net; pub mod num; @@ -583,9 +585,10 @@ pub mod time; #[unstable(feature = "portable_simd", issue = "86656")] mod std_float; -#[doc = include_str!("../../portable-simd/crates/core_simd/src/core_simd_docs.md")] #[unstable(feature = "portable_simd", issue = "86656")] pub mod simd { + #![doc = include_str!("../../portable-simd/crates/core_simd/src/core_simd_docs.md")] + #[doc(inline)] pub use crate::std_float::StdFloat; #[doc(inline)] @@ -716,7 +719,7 @@ pub(crate) mod test_helpers { #[track_caller] pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { use core::hash::{BuildHasher, Hash, Hasher}; - let mut hasher = crate::collections::hash_map::RandomState::new().build_hasher(); + let mut hasher = crate::hash::RandomState::new().build_hasher(); core::panic::Location::caller().hash(&mut hasher); let hc64 = hasher.finish(); let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::<Vec<u8>>(); diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 34b8b6b97..58df83bd7 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -355,15 +355,15 @@ macro_rules! dbg { // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!` // will be malformed. () => { - $crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!()) + $crate::eprintln!("[{}:{}:{}]", $crate::file!(), $crate::line!(), $crate::column!()) }; ($val:expr $(,)?) => { // Use of `match` here is intentional because it affects the lifetimes // of temporaries - https://stackoverflow.com/a/48732525/1063961 match $val { tmp => { - $crate::eprintln!("[{}:{}] {} = {:#?}", - $crate::file!(), $crate::line!(), $crate::stringify!($val), &tmp); + $crate::eprintln!("[{}:{}:{}] {} = {:#?}", + $crate::file!(), $crate::line!(), $crate::column!(), $crate::stringify!($val), &tmp); tmp } } diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index db367cfa0..b24b851a6 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -1,6 +1,6 @@ use crate::fmt; use crate::io::prelude::*; -use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut}; +use crate::io::{BorrowedBuf, IoSlice, IoSliceMut}; use crate::mem::MaybeUninit; use crate::net::test::{next_test_ip4, next_test_ip6}; use crate::net::*; diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index 892fe2ba8..0cf993664 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -1,4 +1,3 @@ -use crate::io::ErrorKind; use crate::net::test::{next_test_ip4, next_test_ip6}; use crate::net::*; use crate::sync::mpsc::channel; diff --git a/library/std/src/os/l4re/raw.rs b/library/std/src/os/l4re/raw.rs index 12c029328..8fb6e99ec 100644 --- a/library/std/src/os/l4re/raw.rs +++ b/library/std/src/os/l4re/raw.rs @@ -31,7 +31,6 @@ pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; target_arch = "powerpc", target_arch = "sparc", target_arch = "arm", - target_arch = "asmjs", target_arch = "wasm32" ))] mod arch { diff --git a/library/std/src/os/linux/process.rs b/library/std/src/os/linux/process.rs index 2b3ff76d7..51af432d0 100644 --- a/library/std/src/os/linux/process.rs +++ b/library/std/src/os/linux/process.rs @@ -152,6 +152,12 @@ pub trait CommandExt: Sealed { /// in a guaranteed race-free manner (e.g. if the `clone3` system call /// is supported). Otherwise, [`pidfd`] will return an error. /// + /// If a pidfd has been successfully created and not been taken from the `Child` + /// then calls to `kill()`, `wait()` and `try_wait()` will use the pidfd + /// instead of the pid. This can prevent pid recycling races, e.g. + /// those caused by rogue libraries in the same process prematurely reaping + /// zombie children via `waitpid(-1, ...)` calls. + /// /// [`Command`]: process::Command /// [`Child`]: process::Child /// [`pidfd`]: fn@ChildExt::pidfd diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index a568f9b26..c29dd62bc 100644 --- a/library/std/src/os/linux/raw.rs +++ b/library/std/src/os/linux/raw.rs @@ -31,7 +31,6 @@ pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; target_arch = "powerpc", target_arch = "sparc", target_arch = "arm", - target_arch = "asmjs", target_arch = "wasm32" ))] mod arch { diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index f82034663..19b4fe220 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -1,8 +1,55 @@ //! SOLID-specific extensions to general I/O primitives +//! +//! Just like raw pointers, raw SOLID Sockets file descriptors point to +//! resources with dynamic lifetimes, and they can dangle if they outlive their +//! resources or be forged if they're created from invalid values. +//! +//! This module provides three types for representing raw file descriptors +//! with different ownership properties: raw, borrowed, and owned, which are +//! analogous to types used for representing pointers: +//! +//! | Type | Analogous to | +//! | ------------------ | ------------ | +//! | [`RawFd`] | `*const _` | +//! | [`BorrowedFd<'a>`] | `&'a _` | +//! | [`OwnedFd`] | `Box<_>` | +//! +//! Like raw pointers, `RawFd` values are primitive values. And in new code, +//! they should be considered unsafe to do I/O on (analogous to dereferencing +//! them). Rust did not always provide this guidance, so existing code in the +//! Rust ecosystem often doesn't mark `RawFd` usage as unsafe. Once the +//! `io_safety` feature is stable, libraries will be encouraged to migrate, +//! either by adding `unsafe` to APIs that dereference `RawFd` values, or by +//! using to `BorrowedFd` or `OwnedFd` instead. +//! +//! Like references, `BorrowedFd` values are tied to a lifetime, to ensure +//! that they don't outlive the resource they point to. These are safe to +//! use. `BorrowedFd` values may be used in APIs which provide safe access to +//! any system call except for: +//! +//! - `close`, because that would end the dynamic lifetime of the resource +//! without ending the lifetime of the file descriptor. +//! +//! - `dup2`/`dup3`, in the second argument, because this argument is +//! closed and assigned a new resource, which may break the assumptions +//! other code using that file descriptor. +//! +//! `BorrowedFd` values may be used in APIs which provide safe access to `dup` +//! system calls, so types implementing `AsFd` or `From<OwnedFd>` should not +//! assume they always have exclusive access to the underlying file +//! description. +//! +//! Like boxes, `OwnedFd` values conceptually own the resource they point to, +//! and free (close) it when they are dropped. +//! +//! [`BorrowedFd<'a>`]: crate::os::solid::io::BorrowedFd #![deny(unsafe_op_in_unsafe_fn)] #![unstable(feature = "solid_ext", issue = "none")] +use crate::fmt; +use crate::marker::PhantomData; +use crate::mem::forget; use crate::net; use crate::sys; use crate::sys_common::{self, AsInner, FromInner, IntoInner}; @@ -10,6 +57,253 @@ use crate::sys_common::{self, AsInner, FromInner, IntoInner}; /// Raw file descriptors. pub type RawFd = i32; +/// A borrowed SOLID Sockets file descriptor. +/// +/// This has a lifetime parameter to tie it to the lifetime of something that +/// owns the socket. +/// +/// This uses `repr(transparent)` and has the representation of a host file +/// descriptor, so it can be used in FFI in places where a socket is passed as +/// an argument, it is not captured or consumed, and it never has the value +/// `SOLID_NET_INVALID_FD`. +/// +/// This type's `.to_owned()` implementation returns another `BorrowedFd` +/// rather than an `OwnedFd`. It just makes a trivial copy of the raw +/// socket, which is then borrowed under the same lifetime. +#[derive(Copy, Clone)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] +#[rustc_nonnull_optimization_guaranteed] +pub struct BorrowedFd<'socket> { + fd: RawFd, + _phantom: PhantomData<&'socket OwnedFd>, +} + +/// An owned SOLID Sockets file descriptor. +/// +/// This closes the file descriptor on drop. +/// +/// This uses `repr(transparent)` and has the representation of a host file +/// descriptor, so it can be used in FFI in places where a socket is passed as +/// an argument, it is not captured or consumed, and it never has the value +/// `SOLID_NET_INVALID_FD`. +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] +#[rustc_nonnull_optimization_guaranteed] +pub struct OwnedFd { + fd: RawFd, +} + +impl BorrowedFd<'_> { + /// Return a `BorrowedFd` holding the given raw file descriptor. + /// + /// # Safety + /// + /// The resource pointed to by `fd` must remain open for the duration of + /// the returned `BorrowedFd`, and it must not have the value + /// `SOLID_NET_INVALID_FD`. + #[inline] + pub const unsafe fn borrow_raw(fd: RawFd) -> Self { + assert!(fd != -1 as RawFd); + // SAFETY: we just asserted that the value is in the valid range and + // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) + unsafe { Self { fd, _phantom: PhantomData } } + } +} + +impl OwnedFd { + /// Creates a new `OwnedFd` instance that shares the same underlying file + /// description as the existing `OwnedFd` instance. + pub fn try_clone(&self) -> crate::io::Result<Self> { + self.as_fd().try_clone_to_owned() + } +} + +impl BorrowedFd<'_> { + /// Creates a new `OwnedFd` instance that shares the same underlying file + /// description as the existing `BorrowedFd` instance. + pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> { + let fd = sys::net::cvt(unsafe { sys::net::netc::dup(self.as_raw_fd()) })?; + Ok(unsafe { OwnedFd::from_raw_fd(fd) }) + } +} + +impl AsRawFd for BorrowedFd<'_> { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +impl AsRawFd for OwnedFd { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +impl IntoRawFd for OwnedFd { + #[inline] + fn into_raw_fd(self) -> RawFd { + let fd = self.fd; + forget(self); + fd + } +} + +impl FromRawFd for OwnedFd { + /// Constructs a new instance of `Self` from the given raw file descriptor. + /// + /// # Safety + /// + /// The resource pointed to by `fd` must be open and suitable for assuming + /// ownership. The resource must not require any cleanup other than `close`. + #[inline] + unsafe fn from_raw_fd(fd: RawFd) -> Self { + assert_ne!(fd, -1 as RawFd); + // SAFETY: we just asserted that the value is in the valid range and + // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) + unsafe { Self { fd } } + } +} + +impl Drop for OwnedFd { + #[inline] + fn drop(&mut self) { + unsafe { sys::net::netc::close(self.fd) }; + } +} + +impl fmt::Debug for BorrowedFd<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BorrowedFd").field("fd", &self.fd).finish() + } +} + +impl fmt::Debug for OwnedFd { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OwnedFd").field("fd", &self.fd).finish() + } +} + +macro_rules! impl_is_terminal { + ($($t:ty),*$(,)?) => {$( + #[unstable(feature = "sealed", issue = "none")] + impl crate::sealed::Sealed for $t {} + + #[stable(feature = "is_terminal", since = "1.70.0")] + impl crate::io::IsTerminal for $t { + #[inline] + fn is_terminal(&self) -> bool { + crate::sys::io::is_terminal(self) + } + } + )*} +} + +impl_is_terminal!(BorrowedFd<'_>, OwnedFd); + +/// A trait to borrow the SOLID Sockets file descriptor from an underlying +/// object. +pub trait AsFd { + /// Borrows the file descriptor. + fn as_fd(&self) -> BorrowedFd<'_>; +} + +impl<T: AsFd> AsFd for &T { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + T::as_fd(self) + } +} + +impl<T: AsFd> AsFd for &mut T { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + T::as_fd(self) + } +} + +impl AsFd for BorrowedFd<'_> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + *self + } +} + +impl AsFd for OwnedFd { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + // Safety: `OwnedFd` and `BorrowedFd` have the same validity + // invariants, and the `BorrowedFd` is bounded by the lifetime + // of `&self`. + unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) } + } +} + +macro_rules! impl_owned_fd_traits { + ($($t:ident)*) => {$( + impl AsFd for net::$t { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().socket().as_fd() + } + } + + impl From<net::$t> for OwnedFd { + #[inline] + fn from(socket: net::$t) -> OwnedFd { + socket.into_inner().into_socket().into_inner() + } + } + + impl From<OwnedFd> for net::$t { + #[inline] + fn from(owned_fd: OwnedFd) -> Self { + Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned_fd))) + } + } + )*}; +} +impl_owned_fd_traits! { TcpStream TcpListener UdpSocket } + +/// This impl allows implementing traits that require `AsFd` on Arc. +/// ``` +/// # #[cfg(target_os = "solid_asp3")] mod group_cfg { +/// # use std::os::solid::io::AsFd; +/// use std::net::UdpSocket; +/// use std::sync::Arc; +/// +/// trait MyTrait: AsFd {} +/// impl MyTrait for Arc<UdpSocket> {} +/// impl MyTrait for Box<UdpSocket> {} +/// # } +/// ``` +impl<T: AsFd> AsFd for crate::sync::Arc<T> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + (**self).as_fd() + } +} + +impl<T: AsFd> AsFd for crate::rc::Rc<T> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + (**self).as_fd() + } +} + +impl<T: AsFd> AsFd for Box<T> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + (**self).as_fd() + } +} + /// A trait to extract the raw SOLID Sockets file descriptor from an underlying /// object. pub trait AsRawFd { @@ -84,7 +378,7 @@ macro_rules! impl_as_raw_fd { impl AsRawFd for net::$t { #[inline] fn as_raw_fd(&self) -> RawFd { - *self.as_inner().socket().as_inner() + self.as_inner().socket().as_raw_fd() } } )*}; @@ -97,7 +391,7 @@ macro_rules! impl_from_raw_fd { impl FromRawFd for net::$t { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> net::$t { - let socket = sys::net::Socket::from_inner(fd); + let socket = unsafe { sys::net::Socket::from_raw_fd(fd) }; net::$t::from_inner(sys_common::net::$t::from_inner(socket)) } } @@ -111,7 +405,7 @@ macro_rules! impl_into_raw_fd { impl IntoRawFd for net::$t { #[inline] fn into_raw_fd(self) -> RawFd { - self.into_inner().into_socket().into_inner() + self.into_inner().into_socket().into_raw_fd() } } )*}; diff --git a/library/std/src/os/solid/mod.rs b/library/std/src/os/solid/mod.rs index 4328ba7c3..0bb83c73d 100644 --- a/library/std/src/os/solid/mod.rs +++ b/library/std/src/os/solid/mod.rs @@ -13,5 +13,5 @@ pub mod prelude { pub use super::ffi::{OsStrExt, OsStringExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; } diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index 274af08a3..b0540872c 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -504,7 +504,7 @@ impl AsHandle for fs::File { impl From<fs::File> for OwnedHandle { #[inline] fn from(file: fs::File) -> OwnedHandle { - file.into_inner().into_inner().into_inner().into() + file.into_inner().into_inner().into_inner() } } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index c80b9e284..65f161f32 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -127,7 +127,7 @@ impl BorrowedSocket<'_> { info.iAddressFamily, info.iSocketType, info.iProtocol, - &mut info, + &info, 0, sys::c::WSA_FLAG_OVERLAPPED | sys::c::WSA_FLAG_NO_HANDLE_INHERIT, ) @@ -147,7 +147,7 @@ impl BorrowedSocket<'_> { info.iAddressFamily, info.iSocketType, info.iProtocol, - &mut info, + &info, 0, sys::c::WSA_FLAG_OVERLAPPED, ) diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index d00e79476..5bf0154ea 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -347,7 +347,7 @@ impl ChildExt for process::Child { /// /// This trait is sealed: it cannot be implemented outside the standard library. /// This is so that future additional methods are not breaking changes. -#[unstable(feature = "windows_process_exit_code_from", issue = "none")] +#[unstable(feature = "windows_process_exit_code_from", issue = "111688")] pub trait ExitCodeExt: Sealed { /// Creates a new `ExitCode` from the raw underlying `u32` return value of /// a process. @@ -355,11 +355,11 @@ pub trait ExitCodeExt: Sealed { /// The exit code should not be 259, as this conflicts with the `STILL_ACTIVE` /// macro returned from the `GetExitCodeProcess` function to signal that the /// process has yet to run to completion. - #[unstable(feature = "windows_process_exit_code_from", issue = "none")] + #[unstable(feature = "windows_process_exit_code_from", issue = "111688")] fn from_raw(raw: u32) -> Self; } -#[unstable(feature = "windows_process_exit_code_from", issue = "none")] +#[unstable(feature = "windows_process_exit_code_from", issue = "111688")] impl ExitCodeExt for process::ExitCode { fn from_raw(raw: u32) -> Self { process::ExitCode::from_inner(From::from(raw)) diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 55f4917a9..66b4ec37c 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -588,7 +588,7 @@ pub fn panicking() -> bool { } /// Entry point of panics from the core crate (`panic_impl` lang item). -#[cfg(not(test))] +#[cfg(not(any(test, doctest)))] #[panic_handler] pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { struct FormatStringPayload<'a> { @@ -669,7 +669,7 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { /// panic!() and assert!(). In particular, this is the only entry point that supports /// arbitrary payloads, not just format strings. #[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")] -#[cfg_attr(not(test), lang = "begin_panic")] +#[cfg_attr(not(any(test, doctest)), lang = "begin_panic")] // lang item for CTFE panic support // never inline unless panic_immediate_abort to avoid code // bloat at the call sites as much as possible diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index f12ffbf2e..fde6ed4f0 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1,10 +1,7 @@ use super::*; -use crate::collections::hash_map::DefaultHasher; use crate::collections::{BTreeSet, HashSet}; -use crate::hash::Hasher; -use crate::rc::Rc; -use crate::sync::Arc; +use crate::hash::DefaultHasher; use core::hint::black_box; #[allow(unknown_lints, unused_macro_rules)] @@ -1461,8 +1458,7 @@ fn test_eq_receivers() { #[test] pub fn test_compare() { - use crate::collections::hash_map::DefaultHasher; - use crate::hash::{Hash, Hasher}; + use crate::hash::{DefaultHasher, Hash, Hasher}; fn hash<T: Hash>(t: T) -> u64 { let mut s = DefaultHasher::new(); diff --git a/library/std/src/process.rs b/library/std/src/process.rs index af6bef1a7..4a7f5d8e0 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1108,7 +1108,7 @@ impl fmt::Debug for Command { /// /// The default format approximates a shell invocation of the program along with its /// arguments. It does not include most of the other command properties. The output is not guaranteed to work - /// (e.g. due to lack of shell-escaping or differences in path resolution) + /// (e.g. due to lack of shell-escaping or differences in path resolution). /// On some platforms you can use [the alternate syntax] to show more fields. /// /// Note that the debug implementation is platform-specific. @@ -2311,7 +2311,7 @@ pub fn id() -> u32 { /// of the `main` function, this trait is likely to be available only on /// standard library's runtime for convenience. Other runtimes are not required /// to provide similar functionality. -#[cfg_attr(not(test), lang = "termination")] +#[cfg_attr(not(any(test, doctest)), lang = "termination")] #[stable(feature = "termination_trait_lib", since = "1.61.0")] #[rustc_on_unimplemented(on( cause = "MainFunctionType", diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 5c83f72f3..335944845 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -154,8 +154,7 @@ fn lang_start_internal( ret_code } -#[cfg(not(test))] -#[inline(never)] +#[cfg(not(any(test, doctest)))] #[lang = "start"] fn lang_start<T: crate::process::Termination + 'static>( main: fn() -> T, diff --git a/library/std/src/sync/mpsc/sync_tests.rs b/library/std/src/sync/mpsc/sync_tests.rs index 632709fd9..945de280f 100644 --- a/library/std/src/sync/mpsc/sync_tests.rs +++ b/library/std/src/sync/mpsc/sync_tests.rs @@ -3,7 +3,6 @@ use crate::env; use crate::rc::Rc; use crate::sync::mpmc::SendTimeoutError; use crate::thread; -use crate::time::Duration; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/mpsc/tests.rs b/library/std/src/sync/mpsc/tests.rs index 1e52a4a70..ac1a804cf 100644 --- a/library/std/src/sync/mpsc/tests.rs +++ b/library/std/src/sync/mpsc/tests.rs @@ -1,7 +1,6 @@ use super::*; use crate::env; use crate::thread; -use crate::time::{Duration, Instant}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index b4ae6b7e0..0c001d7c2 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -146,7 +146,7 @@ use crate::sys::locks as sys; /// let result = data.iter().fold(0, |acc, x| acc + x * 2); /// data.push(result); /// // We drop the `data` explicitly because it's not necessary anymore and the -/// // thread still has work to do. This allow other threads to start working on +/// // thread still has work to do. This allows other threads to start working on /// // the data immediately, without waiting for the rest of the unrelated work /// // to be done here. /// // diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index f49630907..b8873a3b5 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -13,22 +13,65 @@ use crate::sync::Once; /// /// # Examples /// +/// Using `OnceCell` to store a function’s previously computed value (a.k.a. +/// ‘lazy static’ or ‘memoizing’): +/// /// ``` /// use std::sync::OnceLock; /// -/// static CELL: OnceLock<String> = OnceLock::new(); +/// struct DeepThought { +/// answer: String, +/// } +/// +/// impl DeepThought { +/// # fn great_question() -> String { +/// # "42".to_string() +/// # } +/// # +/// fn new() -> Self { +/// Self { +/// // M3 Ultra takes about 16 million years in --release config +/// answer: Self::great_question(), +/// } +/// } +/// } +/// +/// fn computation() -> &'static DeepThought { +/// // n.b. static items do not call [`Drop`] on program termination, so if +/// // [`DeepThought`] impls Drop, that will not be used for this instance. +/// static COMPUTATION: OnceLock<DeepThought> = OnceLock::new(); +/// COMPUTATION.get_or_init(|| DeepThought::new()) +/// } +/// +/// // The `DeepThought` is built, stored in the `OnceLock`, and returned. +/// let _ = computation().answer; +/// // The `DeepThought` is retrieved from the `OnceLock` and returned. +/// let _ = computation().answer; +/// ``` +/// +/// Writing to a `OnceLock` from a separate thread: +/// +/// ``` +/// use std::sync::OnceLock; +/// +/// static CELL: OnceLock<usize> = OnceLock::new(); +/// +/// // `OnceLock` has not been written to yet. /// assert!(CELL.get().is_none()); /// +/// // Spawn a thread and write to `OnceLock`. /// std::thread::spawn(|| { -/// let value: &String = CELL.get_or_init(|| { -/// "Hello, World!".to_string() -/// }); -/// assert_eq!(value, "Hello, World!"); -/// }).join().unwrap(); +/// let value = CELL.get_or_init(|| 12345); +/// assert_eq!(value, &12345); +/// }) +/// .join() +/// .unwrap(); /// -/// let value: Option<&String> = CELL.get(); -/// assert!(value.is_some()); -/// assert_eq!(value.unwrap().as_str(), "Hello, World!"); +/// // `OnceLock` now contains the value. +/// assert_eq!( +/// CELL.get(), +/// Some(&12345), +/// ); /// ``` #[stable(feature = "once_cell", since = "1.70.0")] pub struct OnceLock<T> { diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index ac7c800ff..5d8967bfb 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -532,7 +532,7 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { } #[stable(feature = "std_debug", since = "1.16.0")] -impl<T: fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> { +impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } @@ -546,7 +546,7 @@ impl<T: ?Sized + fmt::Display> fmt::Display for RwLockReadGuard<'_, T> { } #[stable(feature = "std_debug", since = "1.16.0")] -impl<T: fmt::Debug> fmt::Debug for RwLockWriteGuard<'_, T> { +impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLockWriteGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } diff --git a/library/std/src/sys/common/alloc.rs b/library/std/src/sys/common/alloc.rs index d58aa6c27..b7357460f 100644 --- a/library/std/src/sys/common/alloc.rs +++ b/library/std/src/sys/common/alloc.rs @@ -14,7 +14,6 @@ use crate::ptr; target_arch = "powerpc", target_arch = "powerpc64", target_arch = "sparc", - target_arch = "asmjs", target_arch = "wasm32", target_arch = "hexagon", all(target_arch = "riscv32", not(target_os = "espidf")), diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 159ffe7ac..88420bd36 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -53,6 +53,9 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { mod sgx; pub use self::sgx::*; + } else if #[cfg(target_os = "teeos")] { + mod teeos; + pub use self::teeos::*; } else { mod unsupported; pub use self::unsupported::*; diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 386a399f5..d37b8ce63 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -12,7 +12,7 @@ mod dwarf; -#[cfg(not(test))] +#[cfg(not(any(test, doctest)))] cfg_if::cfg_if! { if #[cfg(target_os = "emscripten")] { mod emcc; @@ -28,6 +28,7 @@ cfg_if::cfg_if! { } else if #[cfg(any( all(target_family = "windows", target_env = "gnu"), target_os = "psp", + target_os = "xous", target_os = "solid_asp3", all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re")), all(target_vendor = "fortanix", target_env = "sgx"), diff --git a/library/std/src/sys/solid/net.rs b/library/std/src/sys/solid/net.rs index 1eae0fc06..a768e2406 100644 --- a/library/std/src/sys/solid/net.rs +++ b/library/std/src/sys/solid/net.rs @@ -5,9 +5,10 @@ use crate::{ io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}, mem, net::{Shutdown, SocketAddr}, + os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}, ptr, str, sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}, - sys_common::{AsInner, FromInner, IntoInner}, + sys_common::{FromInner, IntoInner}, time::Duration, }; @@ -28,102 +29,6 @@ const fn max_iov() -> usize { 1024 } -/// A file descriptor. -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] -struct FileDesc { - fd: c_int, -} - -impl FileDesc { - #[inline] - fn new(fd: c_int) -> FileDesc { - assert_ne!(fd, -1i32); - // Safety: we just asserted that the value is in the valid range and - // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { FileDesc { fd } } - } - - #[inline] - fn raw(&self) -> c_int { - self.fd - } - - /// Extracts the actual file descriptor without closing it. - #[inline] - fn into_raw(self) -> c_int { - let fd = self.fd; - mem::forget(self); - fd - } - - fn read(&self, buf: &mut [u8]) -> io::Result<usize> { - let ret = cvt(unsafe { - netc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT)) - })?; - Ok(ret as usize) - } - - fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - let ret = cvt(unsafe { - netc::readv( - self.fd, - bufs.as_ptr() as *const netc::iovec, - cmp::min(bufs.len(), max_iov()) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - true - } - - fn write(&self, buf: &[u8]) -> io::Result<usize> { - let ret = cvt(unsafe { - netc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT)) - })?; - Ok(ret as usize) - } - - fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { - let ret = cvt(unsafe { - netc::writev( - self.fd, - bufs.as_ptr() as *const netc::iovec, - cmp::min(bufs.len(), max_iov()) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - fn duplicate(&self) -> io::Result<FileDesc> { - cvt(unsafe { netc::dup(self.fd) }).map(Self::new) - } -} - -impl AsInner<c_int> for FileDesc { - #[inline] - fn as_inner(&self) -> &c_int { - &self.fd - } -} - -impl Drop for FileDesc { - fn drop(&mut self) { - unsafe { netc::close(self.fd) }; - } -} - #[doc(hidden)] pub trait IsMinusOne { fn is_minus_one(&self) -> bool; @@ -212,7 +117,7 @@ pub(super) fn decode_error_kind(er: abi::ER) -> ErrorKind { pub fn init() {} -pub struct Socket(FileDesc); +pub struct Socket(OwnedFd); impl Socket { pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> { @@ -226,16 +131,13 @@ impl Socket { pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> { unsafe { let fd = cvt(netc::socket(fam, ty, 0))?; - let fd = FileDesc::new(fd); - let socket = Socket(fd); - - Ok(socket) + Ok(Self::from_raw_fd(fd)) } } 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) })?; + cvt(unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; Ok(()) } @@ -264,14 +166,14 @@ impl Socket { timeout.tv_usec = 1; } - let fds = netc::fd_set { num_fds: 1, fds: [self.0.raw()] }; + let fds = netc::fd_set { num_fds: 1, fds: [self.as_raw_fd()] }; let mut writefds = fds; let mut errorfds = fds; let n = unsafe { cvt(netc::select( - self.0.raw() + 1, + self.as_raw_fd() + 1, ptr::null_mut(), &mut writefds, &mut errorfds, @@ -294,18 +196,17 @@ impl Socket { } pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result<Socket> { - let fd = cvt_r(|| unsafe { netc::accept(self.0.raw(), storage, len) })?; - let fd = FileDesc::new(fd); - Ok(Socket(fd)) + let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; + unsafe { Ok(Self::from_raw_fd(fd)) } } pub fn duplicate(&self) -> io::Result<Socket> { - self.0.duplicate().map(Socket) + Ok(Self(self.0.try_clone()?)) } fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { let ret = cvt(unsafe { - netc::recv(self.0.raw(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags) + netc::recv(self.as_raw_fd(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags) })?; unsafe { buf.advance(ret as usize); @@ -330,12 +231,19 @@ impl Socket { } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - self.0.read_vectored(bufs) + let ret = cvt(unsafe { + netc::readv( + self.as_raw_fd(), + bufs.as_ptr() as *const netc::iovec, + cmp::min(bufs.len(), max_iov()) as c_int, + ) + })?; + Ok(ret as usize) } #[inline] pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() + true } fn recv_from_with_flags( @@ -348,7 +256,7 @@ impl Socket { let n = cvt(unsafe { netc::recvfrom( - self.0.raw(), + self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags, @@ -368,16 +276,30 @@ impl Socket { } pub fn write(&self, buf: &[u8]) -> io::Result<usize> { - self.0.write(buf) + let ret = cvt(unsafe { + netc::write( + self.as_raw_fd(), + buf.as_ptr() as *const c_void, + cmp::min(buf.len(), READ_LIMIT), + ) + })?; + Ok(ret as usize) } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { - self.0.write_vectored(bufs) + let ret = cvt(unsafe { + netc::writev( + self.as_raw_fd(), + bufs.as_ptr() as *const netc::iovec, + cmp::min(bufs.len(), max_iov()) as c_int, + ) + })?; + Ok(ret as usize) } #[inline] pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() + true } pub fn set_timeout(&self, dur: Option<Duration>, kind: c_int) -> io::Result<()> { @@ -423,7 +345,7 @@ impl Socket { Shutdown::Read => netc::SHUT_RD, Shutdown::Both => netc::SHUT_RDWR, }; - cvt(unsafe { netc::shutdown(self.0.raw(), how) })?; + cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; Ok(()) } @@ -454,7 +376,7 @@ impl Socket { pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as c_int; cvt(unsafe { - netc::ioctl(*self.as_inner(), netc::FIONBIO, (&mut nonblocking) as *mut c_int as _) + netc::ioctl(self.as_raw_fd(), netc::FIONBIO, (&mut nonblocking) as *mut c_int as _) }) .map(drop) } @@ -466,25 +388,48 @@ impl Socket { // This method is used by sys_common code to abstract over targets. pub fn as_raw(&self) -> c_int { - *self.as_inner() + self.as_raw_fd() } } -impl AsInner<c_int> for Socket { +impl FromInner<OwnedFd> for Socket { #[inline] - fn as_inner(&self) -> &c_int { - self.0.as_inner() + fn from_inner(sock: OwnedFd) -> Socket { + Socket(sock) } } -impl FromInner<c_int> for Socket { - fn from_inner(fd: c_int) -> Socket { - Socket(FileDesc::new(fd)) +impl IntoInner<OwnedFd> for Socket { + #[inline] + fn into_inner(self) -> OwnedFd { + self.0 } } -impl IntoInner<c_int> for Socket { - fn into_inner(self) -> c_int { - self.0.into_raw() +impl AsFd for Socket { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> c_int { + self.0.as_raw_fd() + } +} + +impl FromRawFd for Socket { + #[inline] + unsafe fn from_raw_fd(fd: c_int) -> Socket { + unsafe { Self(FromRawFd::from_raw_fd(fd)) } + } +} + +impl IntoRawFd for Socket { + #[inline] + fn into_raw_fd(self) -> c_int { + self.0.into_raw_fd() } } diff --git a/library/std/src/sys/teeos/alloc.rs b/library/std/src/sys/teeos/alloc.rs new file mode 100644 index 000000000..e236819aa --- /dev/null +++ b/library/std/src/sys/teeos/alloc.rs @@ -0,0 +1,57 @@ +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::ptr; +use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // jemalloc provides alignment less than MIN_ALIGN for small allocations. + // So only rely on MIN_ALIGN if size >= align. + // Also see <https://github.com/rust-lang/rust/issues/45955> and + // <https://github.com/rust-lang/rust/issues/62251#issuecomment-507580914>. + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::malloc(layout.size()) as *mut u8 + } else { + aligned_malloc(&layout) + } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // See the comment above in `alloc` for why this check looks the way it does. + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::calloc(layout.size(), 1) as *mut u8 + } else { + let ptr = self.alloc(layout); + if !ptr.is_null() { + ptr::write_bytes(ptr, 0, layout.size()); + } + ptr + } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + libc::free(ptr as *mut libc::c_void) + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 + } else { + realloc_fallback(self, ptr, layout, new_size) + } + } +} + +#[inline] +unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { + let mut out = ptr::null_mut(); + // posix_memalign requires that the alignment be a multiple of `sizeof(void*)`. + // Since these are all powers of 2, we can just use max. + let align = layout.align().max(crate::mem::size_of::<usize>()); + let ret = libc::posix_memalign(&mut out, align, layout.size()); + if ret != 0 { ptr::null_mut() } else { out as *mut u8 } +} diff --git a/library/std/src/sys/teeos/locks/condvar.rs b/library/std/src/sys/teeos/locks/condvar.rs new file mode 100644 index 000000000..c08e8145b --- /dev/null +++ b/library/std/src/sys/teeos/locks/condvar.rs @@ -0,0 +1,100 @@ +use crate::cell::UnsafeCell; +use crate::ptr; +use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed}; +use crate::sys::locks::mutex::{self, Mutex}; +use crate::sys::time::TIMESPEC_MAX; +use crate::sys_common::lazy_box::{LazyBox, LazyInit}; +use crate::time::Duration; + +extern "C" { + pub fn pthread_cond_timedwait( + cond: *mut libc::pthread_cond_t, + lock: *mut libc::pthread_mutex_t, + adstime: *const libc::timespec, + ) -> libc::c_int; +} + +struct AllocatedCondvar(UnsafeCell<libc::pthread_cond_t>); + +pub struct Condvar { + inner: LazyBox<AllocatedCondvar>, + mutex: AtomicPtr<libc::pthread_mutex_t>, +} + +#[inline] +fn raw(c: &Condvar) -> *mut libc::pthread_cond_t { + c.inner.0.get() +} + +unsafe impl Send for AllocatedCondvar {} +unsafe impl Sync for AllocatedCondvar {} + +impl LazyInit for AllocatedCondvar { + fn init() -> Box<Self> { + let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER))); + + let r = unsafe { libc::pthread_cond_init(condvar.0.get(), crate::ptr::null()) }; + assert_eq!(r, 0); + + condvar + } +} + +impl Drop for AllocatedCondvar { + #[inline] + fn drop(&mut self) { + let r = unsafe { libc::pthread_cond_destroy(self.0.get()) }; + debug_assert_eq!(r, 0); + } +} + +impl Condvar { + pub const fn new() -> Condvar { + Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } + } + + #[inline] + fn verify(&self, mutex: *mut libc::pthread_mutex_t) { + match self.mutex.compare_exchange(ptr::null_mut(), mutex, Relaxed, Relaxed) { + Ok(_) => {} // Stored the address + Err(n) if n == mutex => {} // Lost a race to store the same address + _ => panic!("attempted to use a condition variable with two mutexes"), + } + } + + #[inline] + pub fn notify_one(&self) { + let r = unsafe { libc::pthread_cond_signal(raw(self)) }; + debug_assert_eq!(r, 0); + } + + #[inline] + pub fn notify_all(&self) { + let r = unsafe { libc::pthread_cond_broadcast(raw(self)) }; + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let mutex = mutex::raw(mutex); + self.verify(mutex); + let r = libc::pthread_cond_wait(raw(self), mutex); + debug_assert_eq!(r, 0); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + use crate::sys::time::Timespec; + + let mutex = mutex::raw(mutex); + self.verify(mutex); + + let timeout = Timespec::now(libc::CLOCK_MONOTONIC) + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec()) + .unwrap_or(TIMESPEC_MAX); + + let r = pthread_cond_timedwait(raw(self), mutex, &timeout); + assert!(r == libc::ETIMEDOUT || r == 0); + r == 0 + } +} diff --git a/library/std/src/sys/teeos/locks/mod.rs b/library/std/src/sys/teeos/locks/mod.rs new file mode 100644 index 000000000..c58e9c7fd --- /dev/null +++ b/library/std/src/sys/teeos/locks/mod.rs @@ -0,0 +1,8 @@ +pub mod condvar; +#[path = "../../unix/locks/pthread_mutex.rs"] +pub mod mutex; +pub mod rwlock; + +pub(crate) use condvar::Condvar; +pub(crate) use mutex::Mutex; +pub(crate) use rwlock::RwLock; diff --git a/library/std/src/sys/teeos/locks/rwlock.rs b/library/std/src/sys/teeos/locks/rwlock.rs new file mode 100644 index 000000000..27cdb8878 --- /dev/null +++ b/library/std/src/sys/teeos/locks/rwlock.rs @@ -0,0 +1,44 @@ +use crate::sys::locks::mutex::Mutex; + +/// we do not supported rwlock, so use mutex to simulate rwlock. +/// it's useful because so many code in std will use rwlock. +pub struct RwLock { + inner: Mutex, +} + +impl RwLock { + #[inline] + pub const fn new() -> RwLock { + RwLock { inner: Mutex::new() } + } + + #[inline] + pub fn read(&self) { + unsafe { self.inner.lock() }; + } + + #[inline] + pub fn try_read(&self) -> bool { + unsafe { self.inner.try_lock() } + } + + #[inline] + pub fn write(&self) { + unsafe { self.inner.lock() }; + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + unsafe { self.inner.try_lock() } + } + + #[inline] + pub unsafe fn read_unlock(&self) { + unsafe { self.inner.unlock() }; + } + + #[inline] + pub unsafe fn write_unlock(&self) { + unsafe { self.inner.unlock() }; + } +} diff --git a/library/std/src/sys/teeos/mod.rs b/library/std/src/sys/teeos/mod.rs new file mode 100644 index 000000000..ed8c54b2c --- /dev/null +++ b/library/std/src/sys/teeos/mod.rs @@ -0,0 +1,167 @@ +//! System bindings for the Teeos platform +//! +//! This module contains the facade (aka platform-specific) implementations of +//! OS level functionality for Teeos. +#![allow(unsafe_op_in_unsafe_fn)] +#![allow(unused_variables)] +#![allow(dead_code)] + +pub use self::rand::hashmap_random_keys; + +pub mod alloc; +#[path = "../unsupported/args.rs"] +pub mod args; +#[path = "../unix/cmath.rs"] +pub mod cmath; +#[path = "../unsupported/env.rs"] +pub mod env; +pub mod locks; +//pub mod fd; +#[path = "../unsupported/fs.rs"] +pub mod fs; +#[path = "../unsupported/io.rs"] +pub mod io; +#[path = "../unix/memchr.rs"] +pub mod memchr; +pub mod net; +#[path = "../unsupported/once.rs"] +pub mod once; +pub mod os; +#[path = "../unix/os_str.rs"] +pub mod os_str; +#[path = "../unix/path.rs"] +pub mod path; +#[path = "../unsupported/pipe.rs"] +pub mod pipe; +#[path = "../unsupported/process.rs"] +pub mod process; +mod rand; +pub mod stdio; +pub mod thread; +pub mod thread_local_dtor; +#[path = "../unix/thread_local_key.rs"] +pub mod thread_local_key; +#[path = "../unsupported/thread_parking.rs"] +pub mod thread_parking; +#[allow(non_upper_case_globals)] +#[path = "../unix/time.rs"] +pub mod time; + +use crate::io::ErrorKind; + +pub fn abort_internal() -> ! { + unsafe { libc::abort() } +} + +// Trusted Applications are loaded as dynamic libraries on Teeos, +// so this should never be called. +pub fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {} + +// SAFETY: must be called only once during runtime cleanup. +// this is not guaranteed to run, for example when the program aborts. +pub unsafe fn cleanup() { + unimplemented!() + // We do NOT have stack overflow handler, because TEE OS will kill TA when it happens. + // So cleanup is commented + // stack_overflow::cleanup(); +} + +#[inline] +pub(crate) fn is_interrupted(errno: i32) -> bool { + errno == libc::EINTR +} + +// Note: code below is 1:1 copied from unix/mod.rs +pub fn decode_error_kind(errno: i32) -> ErrorKind { + use ErrorKind::*; + match errno as libc::c_int { + libc::E2BIG => ArgumentListTooLong, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::EBUSY => ResourceBusy, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::EDEADLK => Deadlock, + libc::EDQUOT => FilesystemQuotaExceeded, + libc::EEXIST => AlreadyExists, + libc::EFBIG => FileTooLarge, + libc::EHOSTUNREACH => HostUnreachable, + libc::EINTR => Interrupted, + libc::EINVAL => InvalidInput, + libc::EISDIR => IsADirectory, + libc::ELOOP => FilesystemLoop, + libc::ENOENT => NotFound, + libc::ENOMEM => OutOfMemory, + libc::ENOSPC => StorageFull, + libc::ENOSYS => Unsupported, + libc::EMLINK => TooManyLinks, + libc::ENAMETOOLONG => InvalidFilename, + libc::ENETDOWN => NetworkDown, + libc::ENETUNREACH => NetworkUnreachable, + libc::ENOTCONN => NotConnected, + libc::ENOTDIR => NotADirectory, + libc::ENOTEMPTY => DirectoryNotEmpty, + libc::EPIPE => BrokenPipe, + libc::EROFS => ReadOnlyFilesystem, + libc::ESPIPE => NotSeekable, + libc::ESTALE => StaleNetworkFileHandle, + libc::ETIMEDOUT => TimedOut, + libc::ETXTBSY => ExecutableFileBusy, + libc::EXDEV => CrossesDevices, + + libc::EACCES | libc::EPERM => PermissionDenied, + + // These two constants can have the same value on some systems, + // but different values on others, so we can't use a match + // clause + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, + + _ => Uncategorized, + } +} + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> { + if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } +} + +pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T> +where + T: IsMinusOne, + F: FnMut() -> T, +{ + loop { + match cvt(f()) { + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + other => return other, + } + } +} + +pub fn cvt_nz(error: libc::c_int) -> crate::io::Result<()> { + if error == 0 { Ok(()) } else { Err(crate::io::Error::from_raw_os_error(error)) } +} + +use crate::io as std_io; +pub fn unsupported<T>() -> std_io::Result<T> { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> std_io::Error { + std_io::Error::new(std_io::ErrorKind::Unsupported, "operation not supported on this platform") +} diff --git a/library/std/src/sys/teeos/net.rs b/library/std/src/sys/teeos/net.rs new file mode 100644 index 000000000..0df681dbf --- /dev/null +++ b/library/std/src/sys/teeos/net.rs @@ -0,0 +1,372 @@ +use crate::fmt; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::sys::unsupported; +use crate::time::Duration; + +pub struct TcpStream(!); + +impl TcpStream { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result<Option<Duration>> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result<Option<Duration>> { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { + self.0 + } + + pub fn read(&self, _: &mut [u8]) -> io::Result<usize> { + self.0 + } + + pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn write(&self, _: &[u8]) -> io::Result<usize> { + self.0 + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + self.0 + } + + pub fn socket_addr(&self) -> io::Result<SocketAddr> { + self.0 + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + self.0 + } + + pub fn duplicate(&self) -> io::Result<TcpStream> { + self.0 + } + + pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> { + self.0 + } + + pub fn linger(&self) -> io::Result<Option<Duration>> { + self.0 + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn nodelay(&self) -> io::Result<bool> { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result<u32> { + self.0 + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct TcpListener(!); + +impl TcpListener { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result<SocketAddr> { + self.0 + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + self.0 + } + + pub fn duplicate(&self) -> io::Result<TcpListener> { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result<u32> { + self.0 + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn only_v6(&self) -> io::Result<bool> { + self.0 + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct UdpSocket(!); + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + self.0 + } + + pub fn socket_addr(&self) -> io::Result<SocketAddr> { + self.0 + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> { + self.0 + } + + pub fn duplicate(&self) -> io::Result<UdpSocket> { + self.0 + } + + pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result<Option<Duration>> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result<Option<Duration>> { + self.0 + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn broadcast(&self) -> io::Result<bool> { + self.0 + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v4(&self) -> io::Result<bool> { + self.0 + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn multicast_ttl_v4(&self) -> io::Result<u32> { + self.0 + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v6(&self) -> io::Result<bool> { + self.0 + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result<u32> { + self.0 + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { + self.0 + } + + pub fn send(&self, _: &[u8]) -> io::Result<usize> { + self.0 + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct LookupHost(!); + +impl LookupHost { + pub fn port(&self) -> u16 { + self.0 + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option<SocketAddr> { + self.0 + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &str) -> io::Result<LookupHost> { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> { + unsupported() + } +} + +#[allow(nonstandard_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr {} +} + +pub type Socket = UdpSocket; diff --git a/library/std/src/sys/teeos/os.rs b/library/std/src/sys/teeos/os.rs new file mode 100644 index 000000000..e54a92f01 --- /dev/null +++ b/library/std/src/sys/teeos/os.rs @@ -0,0 +1,134 @@ +//! Implementation of `std::os` functionality for teeos + +use core::marker::PhantomData; + +use crate::error::Error as StdError; +use crate::ffi::{OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::path; +use crate::path::PathBuf; + +use super::unsupported; + +pub fn errno() -> i32 { + unsafe { (*libc::__errno_location()) as i32 } +} + +// Hardcoded to return 4096, since `sysconf` is only implemented as a stub. +pub fn page_size() -> usize { + // unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; + 4096 +} + +// Everything below are stubs and copied from unsupported.rs + +pub fn error_string(_errno: i32) -> String { + "error string unimplemented".to_string() +} + +pub fn getcwd() -> io::Result<PathBuf> { + unsupported() +} + +pub fn chdir(_: &path::Path) -> io::Result<()> { + unsupported() +} + +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option<PathBuf> { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError> +where + I: Iterator<Item = T>, + T: AsRef<OsStr>, +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "not supported on this platform yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "not supported on this platform yet" + } +} + +pub fn current_exe() -> io::Result<PathBuf> { + unsupported() +} + +pub struct Env(!); + +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self(inner) = self; + match *inner {} + } +} + +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + match *inner {} + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + let Self(inner) = self; + match *inner {} + } +} + +pub fn env() -> Env { + panic!("not supported on this platform") +} + +pub fn getenv(_: &OsStr) -> Option<OsString> { + None +} + +pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { + Err(io::Error::new(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +} + +pub fn unsetenv(_: &OsStr) -> io::Result<()> { + Err(io::Error::new(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem on this platform") +} + +pub fn home_dir() -> Option<PathBuf> { + None +} + +pub fn exit(_code: i32) -> ! { + panic!("TA should not call `exit`") +} + +pub fn getpid() -> u32 { + panic!("no pids on this platform") +} diff --git a/library/std/src/sys/teeos/rand.rs b/library/std/src/sys/teeos/rand.rs new file mode 100644 index 000000000..b45c3bb40 --- /dev/null +++ b/library/std/src/sys/teeos/rand.rs @@ -0,0 +1,21 @@ +pub fn hashmap_random_keys() -> (u64, u64) { + const KEY_LEN: usize = core::mem::size_of::<u64>(); + + let mut v = [0u8; KEY_LEN * 2]; + imp::fill_bytes(&mut v); + + let key1 = v[0..KEY_LEN].try_into().unwrap(); + let key2 = v[KEY_LEN..].try_into().unwrap(); + + (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) +} + +mod imp { + extern "C" { + fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t); + } + + pub fn fill_bytes(v: &mut [u8]) { + unsafe { TEE_GenerateRandom(v.as_mut_ptr() as _, v.len() * crate::mem::size_of::<u8>()) } + } +} diff --git a/library/std/src/sys/teeos/stdio.rs b/library/std/src/sys/teeos/stdio.rs new file mode 100644 index 000000000..9ca04f292 --- /dev/null +++ b/library/std/src/sys/teeos/stdio.rs @@ -0,0 +1,88 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::io; +use core::arch::asm; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +const KCALL_DEBUG_CMD_PUT_BYTES: i64 = 2; + +unsafe fn debug_call(cap_ref: u64, call_no: i64, arg1: u64, arg2: u64) -> i32 { + let ret: u64; + unsafe { + asm!( + "svc #99", + inout("x0") cap_ref => ret, + in("x1") call_no, + in("x2") arg1, + in("x3") arg2, + ); + } + + ret as i32 +} + +fn print_buf(s: &[u8]) -> io::Result<usize> { + // Corresponds to `HM_DEBUG_PUT_BYTES_LIMIT`. + const MAX_LEN: usize = 512; + let len = if s.len() > MAX_LEN { MAX_LEN } else { s.len() }; + let result = unsafe { debug_call(0, KCALL_DEBUG_CMD_PUT_BYTES, s.as_ptr() as u64, len as u64) }; + + if result == 0 { Ok(len) } else { Err(io::Error::from(io::ErrorKind::InvalidInput)) } +} + +impl Stdin { + pub const fn new() -> Stdin { + Stdin + } +} + +impl io::Read for Stdin { + fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> { + Ok(0) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + print_buf(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> { + print_buf(buf) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = 0; + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(libc::EBADF as i32) +} + +pub fn panic_output() -> Option<impl io::Write> { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/teeos/thread.rs b/library/std/src/sys/teeos/thread.rs new file mode 100644 index 000000000..155f333f9 --- /dev/null +++ b/library/std/src/sys/teeos/thread.rs @@ -0,0 +1,164 @@ +use core::convert::TryInto; + +use crate::cmp; +use crate::ffi::CStr; +use crate::io; +use crate::mem; +use crate::num::NonZeroUsize; +use crate::ptr; +use crate::sys::os; +use crate::time::Duration; + +pub const DEFAULT_MIN_STACK_SIZE: usize = 8 * 1024; + +pub struct Thread { + id: libc::pthread_t, +} + +// Some platforms may have pthread_t as a pointer in which case we still want +// a thread to be Send/Sync +unsafe impl Send for Thread {} +unsafe impl Sync for Thread {} + +extern "C" { + pub fn TEE_Wait(timeout: u32) -> u32; +} + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> { + let p = Box::into_raw(Box::new(p)); + let mut native: libc::pthread_t = mem::zeroed(); + let mut attr: libc::pthread_attr_t = mem::zeroed(); + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + assert_eq!( + libc::pthread_attr_settee( + &mut attr, + libc::TEESMP_THREAD_ATTR_CA_INHERIT, + libc::TEESMP_THREAD_ATTR_TASK_ID_INHERIT, + libc::TEESMP_THREAD_ATTR_HAS_SHADOW, + ), + 0, + ); + + let stack_size = cmp::max(stack, min_stack_size(&attr)); + + match libc::pthread_attr_setstacksize(&mut attr, stack_size) { + 0 => {} + n => { + assert_eq!(n, libc::EINVAL); + // EINVAL means |stack_size| is either too small or not a + // multiple of the system page size. Because it's definitely + // >= PTHREAD_STACK_MIN, it must be an alignment issue. + // Round up to the nearest page and try again. + let page_size = os::page_size(); + let stack_size = + (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); + assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0); + } + }; + + let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _); + // Note: if the thread creation fails and this assert fails, then p will + // be leaked. However, an alternative design could cause double-free + // which is clearly worse. + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + + return if ret != 0 { + // The thread failed to start and as a result p was not consumed. Therefore, it is + // safe to reconstruct the box so that it gets deallocated. + drop(Box::from_raw(p)); + Err(io::Error::from_raw_os_error(ret)) + } else { + // The new thread will start running earliest after the next yield. + // We add a yield here, so that the user does not have to. + Thread::yield_now(); + Ok(Thread { id: native }) + }; + + extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { + unsafe { + // Next, set up our stack overflow handler which may get triggered if we run + // out of stack. + // this is not necessary in TEE. + //let _handler = stack_overflow::Handler::new(); + // Finally, let's run some code. + Box::from_raw(main as *mut Box<dyn FnOnce()>)(); + } + ptr::null_mut() + } + } + + pub fn yield_now() { + let ret = unsafe { libc::sched_yield() }; + debug_assert_eq!(ret, 0); + } + + /// This does not do anything on teeos + pub fn set_name(_name: &CStr) { + // Both pthread_setname_np and prctl are not available to the TA, + // so we can't implement this currently. If the need arises please + // contact the teeos rustzone team. + } + + /// only main thread could wait for sometime in teeos + pub fn sleep(dur: Duration) { + let sleep_millis = dur.as_millis(); + let final_sleep: u32 = + if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 }; + unsafe { + let _ = TEE_Wait(final_sleep); + } + } + + /// must join, because no pthread_detach supported + pub fn join(self) { + unsafe { + let ret = libc::pthread_join(self.id, ptr::null_mut()); + mem::forget(self); + assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); + } + } + + pub fn id(&self) -> libc::pthread_t { + self.id + } + + pub fn into_id(self) -> libc::pthread_t { + let id = self.id; + mem::forget(self); + id + } +} + +impl Drop for Thread { + fn drop(&mut self) { + // we can not call detach, so just panic if thread spawn without join + panic!("thread must join, detach is not supported!"); + } +} + +// Note: Both `sched_getaffinity` and `sysconf` are available but not functional on +// teeos, so this function always returns an Error! +pub fn available_parallelism() -> io::Result<NonZeroUsize> { + Err(io::Error::new( + io::ErrorKind::NotFound, + "The number of hardware threads is not known for the target platform", + )) +} + +// stub +pub mod guard { + use crate::ops::Range; + pub type Guard = Range<usize>; + pub unsafe fn current() -> Option<Guard> { + None + } + pub unsafe fn init() -> Option<Guard> { + None + } +} + +fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { + libc::PTHREAD_STACK_MIN.try_into().expect("Infallible") +} diff --git a/library/std/src/sys/teeos/thread_local_dtor.rs b/library/std/src/sys/teeos/thread_local_dtor.rs new file mode 100644 index 000000000..5c6bc4d67 --- /dev/null +++ b/library/std/src/sys/teeos/thread_local_dtor.rs @@ -0,0 +1,4 @@ +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; + register_dtor_fallback(t, dtor); +} diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index 2da17fabc..9f7dcc041 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -244,13 +244,15 @@ mod imp { let mut res = Vec::new(); unsafe { - let process_info_sel = sel_registerName("processInfo\0".as_ptr()); - let arguments_sel = sel_registerName("arguments\0".as_ptr()); - let utf8_sel = sel_registerName("UTF8String\0".as_ptr()); - let count_sel = sel_registerName("count\0".as_ptr()); - let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr()); - - let klass = objc_getClass("NSProcessInfo\0".as_ptr()); + let process_info_sel = + sel_registerName(c"processInfo".as_ptr() as *const libc::c_uchar); + let arguments_sel = sel_registerName(c"arguments".as_ptr() as *const libc::c_uchar); + let utf8_sel = sel_registerName(c"UTF8String".as_ptr() as *const libc::c_uchar); + let count_sel = sel_registerName(c"count".as_ptr() as *const libc::c_uchar); + let object_at_sel = + sel_registerName(c"objectAtIndex:".as_ptr() as *const libc::c_uchar); + + let klass = objc_getClass(c"NSProcessInfo".as_ptr() as *const libc::c_uchar); let info = objc_msgSend(klass, process_info_sel); let args = objc_msgSend(info, arguments_sel); diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs index 3bb492fa9..3d4ba5098 100644 --- a/library/std/src/sys/unix/env.rs +++ b/library/std/src/sys/unix/env.rs @@ -174,17 +174,6 @@ pub mod os { pub const EXE_EXTENSION: &str = "elf"; } -#[cfg(all(target_os = "emscripten", target_arch = "asmjs"))] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "emscripten"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ".js"; - pub const EXE_EXTENSION: &str = "js"; -} - #[cfg(all(target_os = "emscripten", target_arch = "wasm32"))] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 40eb910fd..72e7b1b1f 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1140,7 +1140,7 @@ impl File { cfg_has_statx! { if let Some(ret) = unsafe { try_statx( fd, - b"\0" as *const _ as *const c_char, + c"".as_ptr() as *const c_char, libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, libc::STATX_ALL, ) } { @@ -1300,13 +1300,17 @@ impl File { pub fn set_times(&self, times: FileTimes) -> io::Result<()> { #[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))] - let to_timespec = |time: Option<SystemTime>| { - match time { - Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), - Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time")), - Some(_) => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too small to set as a file time")), - None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), - } + let to_timespec = |time: Option<SystemTime>| match time { + Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), + Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time" + )), + Some(_) => Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "timestamp is too small to set as a file time" + )), + None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), }; cfg_if::cfg_if! { if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))] { diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 4b28f6feb..b5da5f870 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -1,6 +1,5 @@ #![allow(missing_docs, nonstandard_style)] -use crate::ffi::CStr; use crate::io::ErrorKind; pub use self::rand::hashmap_random_keys; @@ -75,7 +74,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // thread-id for the main thread and so renaming the main thread will rename the // process and we only want to enable this on platforms we've tested. if cfg!(target_os = "macos") { - thread::Thread::set_name(&CStr::from_bytes_with_nul_unchecked(b"main\0")); + thread::Thread::set_name(&c"main"); } unsafe fn sanitize_standard_fds() { @@ -127,7 +126,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { if pfd.revents & libc::POLLNVAL == 0 { continue; } - if open64("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 { + if open64(c"/dev/null".as_ptr().cast(), libc::O_RDWR, 0) == -1 { // If the stream is closed but we failed to reopen it, abort the // process. Otherwise we wouldn't preserve the safety of // operations on the corresponding Rust object Stdin, Stdout, or @@ -157,7 +156,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { use libc::open64; for fd in 0..3 { if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF { - if open64("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 { + if open64(c"/dev/null".as_ptr().cast(), libc::O_RDWR, 0) == -1 { // If the stream is closed but we failed to reopen it, abort the // process. Otherwise we wouldn't preserve the safety of // operations on the corresponding Rust object Stdin, Stdout, or diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index dc3c037c0..881b3a25c 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -180,7 +180,7 @@ pub fn getcwd() -> io::Result<PathBuf> { } #[cfg(target_os = "espidf")] -pub fn chdir(p: &path::Path) -> io::Result<()> { +pub fn chdir(_p: &path::Path) -> io::Result<()> { super::unsupported::unsupported() } @@ -274,15 +274,19 @@ pub fn current_exe() -> io::Result<PathBuf> { return path.canonicalize(); } // Search PWD to infer current_exe. - if let Some(pstr) = path.to_str() && pstr.contains("/") { + 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 { + if pb.is_file() + && let Ok(metadata) = crate::fs::metadata(&pb) + && metadata.permissions().mode() & 0o111 != 0 + { return pb.canonicalize(); } } diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index bac32d9e6..c5f04fb8b 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -24,11 +24,11 @@ cfg_if::cfg_if! { if #[cfg(target_os = "fuchsia")] { // fuchsia doesn't have /dev/null } else if #[cfg(target_os = "redox")] { - const DEV_NULL: &str = "null:\0"; + const DEV_NULL: &CStr = c"null:"; } else if #[cfg(target_os = "vxworks")] { - const DEV_NULL: &str = "/null\0"; + const DEV_NULL: &CStr = c"/null"; } else { - const DEV_NULL: &str = "/dev/null\0"; + const DEV_NULL: &CStr = c"/dev/null"; } } @@ -481,8 +481,7 @@ impl Stdio { let mut opts = OpenOptions::new(); opts.read(readable); opts.write(!readable); - let path = unsafe { CStr::from_ptr(DEV_NULL.as_ptr() as *const _) }; - let fd = File::open_c(&path, &opts)?; + let fd = File::open_c(DEV_NULL, &opts)?; Ok((ChildStdio::Owned(fd.into_inner()), None)) } diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 72aca4e66..ee86a5f88 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -9,6 +9,8 @@ use core::ffi::NonZero_c_int; #[cfg(target_os = "linux")] use crate::os::linux::process::PidFd; +#[cfg(target_os = "linux")] +use crate::os::unix::io::AsRawFd; #[cfg(any( target_os = "macos", @@ -696,11 +698,12 @@ impl Command { msg.msg_iov = &mut iov as *mut _ as *mut _; msg.msg_iovlen = 1; - msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _; - msg.msg_control = &mut cmsg.buf as *mut _ as *mut _; // only attach cmsg if we successfully acquired the pidfd if pidfd >= 0 { + msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _; + msg.msg_control = &mut cmsg.buf as *mut _ as *mut _; + let hdr = CMSG_FIRSTHDR(&mut msg as *mut _ as *mut _); (*hdr).cmsg_level = SOL_SOCKET; (*hdr).cmsg_type = SCM_RIGHTS; @@ -717,7 +720,7 @@ impl Command { // so we get a consistent SEQPACKET order match cvt_r(|| libc::sendmsg(sock.as_raw(), &msg, 0)) { Ok(0) => {} - _ => rtabort!("failed to communicate with parent process"), + other => rtabort!("failed to communicate with parent process. {:?}", other), } } } @@ -748,7 +751,7 @@ impl Command { msg.msg_controllen = mem::size_of::<Cmsg>() as _; msg.msg_control = &mut cmsg as *mut _ as *mut _; - match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, 0)) { + match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, libc::MSG_CMSG_CLOEXEC)) { Err(_) => return -1, Ok(_) => {} } @@ -787,7 +790,7 @@ pub struct Process { // On Linux, stores the pidfd created for this child. // This is None if the user did not request pidfd creation, // or if the pidfd could not be created for some reason - // (e.g. the `clone3` syscall was not available). + // (e.g. the `pidfd_open` syscall was not available). #[cfg(target_os = "linux")] pidfd: Option<PidFd>, } @@ -816,10 +819,23 @@ impl Process { // and used for another process, and we probably shouldn't be killing // random processes, so return Ok because the process has exited already. if self.status.is_some() { - Ok(()) - } else { - cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) + return Ok(()); + } + #[cfg(target_os = "linux")] + if let Some(pid_fd) = self.pidfd.as_ref() { + // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too + return cvt(unsafe { + libc::syscall( + libc::SYS_pidfd_send_signal, + pid_fd.as_raw_fd(), + libc::SIGKILL, + crate::ptr::null::<()>(), + 0, + ) + }) + .map(drop); } + cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) } pub fn wait(&mut self) -> io::Result<ExitStatus> { @@ -827,6 +843,17 @@ impl Process { if let Some(status) = self.status { return Ok(status); } + #[cfg(target_os = "linux")] + if let Some(pid_fd) = self.pidfd.as_ref() { + let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; + + cvt_r(|| unsafe { + libc::waitid(libc::P_PIDFD, pid_fd.as_raw_fd() as u32, &mut siginfo, libc::WEXITED) + })?; + let status = ExitStatus::from_waitid_siginfo(siginfo); + self.status = Some(status); + return Ok(status); + } let mut status = 0 as c_int; cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; self.status = Some(ExitStatus::new(status)); @@ -837,6 +864,25 @@ impl Process { if let Some(status) = self.status { return Ok(Some(status)); } + #[cfg(target_os = "linux")] + if let Some(pid_fd) = self.pidfd.as_ref() { + let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; + + cvt(unsafe { + libc::waitid( + libc::P_PIDFD, + pid_fd.as_raw_fd() as u32, + &mut siginfo, + libc::WEXITED | libc::WNOHANG, + ) + })?; + if unsafe { siginfo.si_pid() } == 0 { + return Ok(None); + } + let status = ExitStatus::from_waitid_siginfo(siginfo); + self.status = Some(status); + return Ok(Some(status)); + } let mut status = 0 as c_int; let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?; if pid == 0 { @@ -866,6 +912,20 @@ impl ExitStatus { ExitStatus(status) } + #[cfg(target_os = "linux")] + pub fn from_waitid_siginfo(siginfo: libc::siginfo_t) -> ExitStatus { + let status = unsafe { siginfo.si_status() }; + + match siginfo.si_code { + libc::CLD_EXITED => ExitStatus((status & 0xff) << 8), + libc::CLD_KILLED => ExitStatus(status), + libc::CLD_DUMPED => ExitStatus(status | 0x80), + libc::CLD_CONTINUED => ExitStatus(0xffff), + libc::CLD_STOPPED | libc::CLD_TRAPPED => ExitStatus(((status & 0xff) << 8) | 0x7f), + _ => unreachable!("waitid() should only return the above codes"), + } + } + fn exited(&self) -> bool { libc::WIFEXITED(self.0) } diff --git a/library/std/src/sys/unix/process/process_unix/tests.rs b/library/std/src/sys/unix/process/process_unix/tests.rs index 6aa79e7f9..6e952ed7c 100644 --- a/library/std/src/sys/unix/process/process_unix/tests.rs +++ b/library/std/src/sys/unix/process/process_unix/tests.rs @@ -64,7 +64,8 @@ fn test_command_fork_no_unwind() { #[test] #[cfg(target_os = "linux")] fn test_command_pidfd() { - use crate::os::fd::RawFd; + use crate::assert_matches::assert_matches; + use crate::os::fd::{AsRawFd, RawFd}; use crate::os::linux::process::{ChildExt, CommandExt}; use crate::process::Command; @@ -78,10 +79,22 @@ fn test_command_pidfd() { }; // always exercise creation attempts - let child = Command::new("echo").create_pidfd(true).spawn().unwrap(); + let mut child = Command::new("false").create_pidfd(true).spawn().unwrap(); // but only check if we know that the kernel supports pidfds if pidfd_open_available { - assert!(child.pidfd().is_ok()) + assert!(child.pidfd().is_ok()); } + if let Ok(pidfd) = child.pidfd() { + let flags = super::cvt(unsafe { libc::fcntl(pidfd.as_raw_fd(), libc::F_GETFD) }).unwrap(); + assert!(flags & libc::FD_CLOEXEC != 0); + } + let status = child.wait().expect("error waiting on pidfd"); + assert_eq!(status.code(), Some(1)); + + let mut child = Command::new("sleep").arg("1000").create_pidfd(true).spawn().unwrap(); + assert_matches!(child.try_wait(), Ok(None)); + child.kill().expect("failed to kill child"); + let status = child.wait().expect("error waiting on pidfd"); + assert_eq!(status.signal(), Some(libc::SIGKILL)); } diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 29db9468e..76b96bb37 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -163,10 +163,9 @@ impl Thread { #[cfg(target_os = "netbsd")] pub fn set_name(name: &CStr) { unsafe { - let cname = CStr::from_bytes_with_nul_unchecked(b"%s\0".as_slice()); let res = libc::pthread_setname_np( libc::pthread_self(), - cname.as_ptr(), + c"%s".as_ptr(), name.as_ptr() as *mut libc::c_void, ); debug_assert_eq!(res, 0); diff --git a/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs index 06399e8a2..ac85531c3 100644 --- a/library/std/src/sys/unix/thread_local_dtor.rs +++ b/library/std/src/sys/unix/thread_local_dtor.rs @@ -12,7 +12,13 @@ // 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"))] +#[cfg(any( + target_os = "linux", + target_os = "android", + 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)] @@ -23,6 +29,8 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { /// 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(sanitizer_cfi_normalize_integers)] + use core::ffi::c_int; #[cfg(not(sanitizer_cfi_normalize_integers))] #[cfi_encoding = "i"] #[repr(transparent)] diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index f2e86a4fb..f62eb828e 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -23,11 +23,11 @@ struct Nanoseconds(u32); #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SystemTime { - pub(in crate::sys::unix) t: Timespec, + pub(crate) t: Timespec, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(in crate::sys::unix) struct Timespec { +pub(crate) struct Timespec { tv_sec: i64, tv_nsec: Nanoseconds, } @@ -239,11 +239,11 @@ impl From<libc::timespec> for Timespec { not(target_arch = "riscv32") ))] #[repr(C)] -pub(in crate::sys::unix) struct __timespec64 { - pub(in crate::sys::unix) tv_sec: i64, +pub(crate) struct __timespec64 { + pub(crate) tv_sec: i64, #[cfg(target_endian = "big")] _padding: i32, - pub(in crate::sys::unix) tv_nsec: i32, + pub(crate) tv_nsec: i32, #[cfg(target_endian = "little")] _padding: i32, } @@ -255,7 +255,7 @@ pub(in crate::sys::unix) struct __timespec64 { not(target_arch = "riscv32") ))] impl __timespec64 { - pub(in crate::sys::unix) fn new(tv_sec: i64, tv_nsec: i32) -> Self { + pub(crate) fn new(tv_sec: i64, tv_nsec: i32) -> Self { Self { tv_sec, tv_nsec, _padding: 0 } } } diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index 437aae3ae..e82386654 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -477,12 +477,13 @@ impl File { } pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - let to_timestamp = |time: Option<SystemTime>| { - match time { - Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), - Some(_) => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time")), - None => Ok(0), - } + let to_timestamp = |time: Option<SystemTime>| match time { + Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), + Some(_) => Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time" + )), + None => Ok(0), }; self.fd.filestat_set_times( to_timestamp(times.accessed)?, diff --git a/library/std/src/sys/windows/api.rs b/library/std/src/sys/windows/api.rs index e9f0bbfbe..a7ea59e85 100644 --- a/library/std/src/sys/windows/api.rs +++ b/library/std/src/sys/windows/api.rs @@ -48,7 +48,7 @@ use super::c; /// 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. + // Const assert that the size does not exceed u32::MAX. // Uses a trait to workaround restriction on using generic types in inner items. trait Win32SizeOf: Sized { const WIN32_SIZE_OF: u32 = { @@ -132,7 +132,7 @@ pub fn set_file_information_by_handle<T: SetFileInformation>( size: u32, ) -> Result<(), WinError> { let result = c::SetFileInformationByHandle(handle, class, info, size); - (result != 0).then_some(()).ok_or_else(|| get_last_error()) + (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()) } diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index a349e24b0..d55d9bace 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -3,6 +3,7 @@ #![allow(nonstandard_style)] #![cfg_attr(test, allow(dead_code))] #![unstable(issue = "none", feature = "windows_c")] +#![allow(clippy::style)] use crate::ffi::CStr; use crate::mem; @@ -46,6 +47,8 @@ pub use FD_SET as fd_set; pub use LINGER as linger; pub use TIMEVAL as timeval; +pub const INVALID_HANDLE_VALUE: HANDLE = ::core::ptr::invalid_mut(-1i32 as _); + // 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; @@ -81,7 +84,7 @@ pub fn nt_success(status: NTSTATUS) -> bool { impl UNICODE_STRING { pub fn from_ref(slice: &[u16]) -> Self { - let len = slice.len() * mem::size_of::<u16>(); + let len = mem::size_of_val(slice); Self { Length: len as _, MaximumLength: len as _, Buffer: slice.as_ptr() as _ } } } @@ -321,7 +324,7 @@ pub unsafe fn NtWriteFile( // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. compat_fn_with_fallback! { - pub static KERNEL32: &CStr = ansi_str!("kernel32"); + pub static KERNEL32: &CStr = c"kernel32"; // >= Win10 1607 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription @@ -354,7 +357,7 @@ compat_fn_optional! { } compat_fn_with_fallback! { - pub static NTDLL: &CStr = ansi_str!("ntdll"); + pub static NTDLL: &CStr = c"ntdll"; pub fn NtCreateKeyedEvent( KeyedEventHandle: LPHANDLE, diff --git a/library/std/src/sys/windows/c/windows_sys.lst b/library/std/src/sys/windows/c/windows_sys.lst index 38bf15b7c..f91e1054a 100644 --- a/library/std/src/sys/windows/c/windows_sys.lst +++ b/library/std/src/sys/windows/c/windows_sys.lst @@ -2,6 +2,7 @@ --config flatten std --filter // tidy-alphabetical-start +!Windows.Win32.Foundation.INVALID_HANDLE_VALUE Windows.Wdk.Storage.FileSystem.FILE_COMPLETE_IF_OPLOCKED Windows.Wdk.Storage.FileSystem.FILE_CONTAINS_EXTENDED_CREATE_INFORMATION Windows.Wdk.Storage.FileSystem.FILE_CREATE @@ -1923,7 +1924,6 @@ Windows.Win32.Foundation.HANDLE_FLAG_INHERIT Windows.Win32.Foundation.HANDLE_FLAG_PROTECT_FROM_CLOSE Windows.Win32.Foundation.HANDLE_FLAGS Windows.Win32.Foundation.HMODULE -Windows.Win32.Foundation.INVALID_HANDLE_VALUE Windows.Win32.Foundation.MAX_PATH Windows.Win32.Foundation.NO_ERROR Windows.Win32.Foundation.NTSTATUS @@ -2483,7 +2483,6 @@ Windows.Win32.System.SystemInformation.GetSystemTimeAsFileTime Windows.Win32.System.SystemInformation.GetWindowsDirectoryW Windows.Win32.System.SystemInformation.PROCESSOR_ARCHITECTURE Windows.Win32.System.SystemInformation.SYSTEM_INFO -Windows.Win32.System.SystemServices.ALL_PROCESSOR_GROUPS Windows.Win32.System.SystemServices.DLL_PROCESS_DETACH Windows.Win32.System.SystemServices.DLL_THREAD_DETACH Windows.Win32.System.SystemServices.EXCEPTION_MAXIMUM_PARAMETERS @@ -2492,6 +2491,7 @@ Windows.Win32.System.SystemServices.IO_REPARSE_TAG_SYMLINK Windows.Win32.System.Threading.ABOVE_NORMAL_PRIORITY_CLASS Windows.Win32.System.Threading.AcquireSRWLockExclusive Windows.Win32.System.Threading.AcquireSRWLockShared +Windows.Win32.System.Threading.ALL_PROCESSOR_GROUPS Windows.Win32.System.Threading.BELOW_NORMAL_PRIORITY_CLASS Windows.Win32.System.Threading.CREATE_BREAKAWAY_FROM_JOB Windows.Win32.System.Threading.CREATE_DEFAULT_ERROR_MODE diff --git a/library/std/src/sys/windows/c/windows_sys.rs b/library/std/src/sys/windows/c/windows_sys.rs index e0509e6a5..b38b70c89 100644 --- a/library/std/src/sys/windows/c/windows_sys.rs +++ b/library/std/src/sys/windows/c/windows_sys.rs @@ -4,7 +4,7 @@ // regenerate the bindings. // // ignore-tidy-filelength -// Bindings generated by `windows-bindgen` 0.51.1 +// Bindings generated by `windows-bindgen` 0.52.0 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] #[link(name = "advapi32")] @@ -63,7 +63,7 @@ extern "system" { lpnewfilename: PCWSTR, lpprogressroutine: LPPROGRESS_ROUTINE, lpdata: *const ::core::ffi::c_void, - pbcancel: *mut i32, + pbcancel: *mut BOOL, dwcopyflags: u32, ) -> BOOL; } @@ -619,7 +619,7 @@ extern "system" { lpmultibytestr: PSTR, cbmultibyte: i32, lpdefaultchar: PCSTR, - lpuseddefaultchar: *mut i32, + lpuseddefaultchar: *mut BOOL, ) -> i32; } #[link(name = "kernel32")] @@ -869,7 +869,7 @@ 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; +pub const ALL_PROCESSOR_GROUPS: u16 = 65535u16; #[repr(C)] pub union ARM64_NT_NEON128 { pub Anonymous: ARM64_NT_NEON128_0, @@ -3498,7 +3498,6 @@ impl ::core::clone::Clone for INIT_ONCE { } pub const INIT_ONCE_INIT_FAILED: u32 = 4u32; pub const INVALID_FILE_ATTRIBUTES: u32 = 4294967295u32; -pub const INVALID_HANDLE_VALUE: HANDLE = ::core::ptr::invalid_mut(-1i32 as _); pub const INVALID_SOCKET: SOCKET = -1i32 as _; #[repr(C)] pub struct IN_ADDR { diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index e28dd4935..f60b3a2c7 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -225,9 +225,9 @@ macro_rules! compat_fn_optional { /// Load all needed functions from "api-ms-win-core-synch-l1-2-0". pub(super) fn load_synch_functions() { fn try_load() -> Option<()> { - const MODULE_NAME: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0"); - const WAIT_ON_ADDRESS: &CStr = ansi_str!("WaitOnAddress"); - const WAKE_BY_ADDRESS_SINGLE: &CStr = ansi_str!("WakeByAddressSingle"); + const MODULE_NAME: &CStr = c"api-ms-win-core-synch-l1-2-0"; + const WAIT_ON_ADDRESS: &CStr = c"WaitOnAddress"; + const WAKE_BY_ADDRESS_SINGLE: &CStr = c"WakeByAddressSingle"; // Try loading the library and all the required functions. // If any step fails, then they all fail. diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index d7e36b9a3..424845436 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -1,7 +1,7 @@ use crate::os::windows::prelude::*; use crate::borrow::Cow; -use crate::ffi::OsString; +use crate::ffi::{c_void, OsString}; use crate::fmt; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem::{self, MaybeUninit}; @@ -16,8 +16,6 @@ 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::{api, to_u16s, IoResult}; @@ -156,7 +154,7 @@ impl DirEntry { } pub fn path(&self) -> PathBuf { - self.root.join(&self.file_name()) + self.root.join(self.file_name()) } pub fn file_name(&self) -> OsString { @@ -273,7 +271,9 @@ impl OpenOptions { (false, false, false) => c::OPEN_EXISTING, (true, false, false) => c::OPEN_ALWAYS, (false, true, false) => c::TRUNCATE_EXISTING, - (true, true, false) => c::CREATE_ALWAYS, + // `CREATE_ALWAYS` has weird semantics so we emulate it using + // `OPEN_ALWAYS` and a manual truncation step. See #115745. + (true, true, false) => c::OPEN_ALWAYS, (_, _, true) => c::CREATE_NEW, }) } @@ -289,19 +289,42 @@ impl OpenOptions { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { let path = maybe_verbatim(path)?; + let creation = opts.get_creation_mode()?; let handle = unsafe { c::CreateFileW( path.as_ptr(), opts.get_access_mode()?, opts.share_mode, opts.security_attributes, - opts.get_creation_mode()?, + creation, opts.get_flags_and_attributes(), ptr::null_mut(), ) }; let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) }; - if let Ok(handle) = handle.try_into() { + if let Ok(handle) = OwnedHandle::try_from(handle) { + // Manual truncation. See #115745. + if opts.truncate + && creation == c::OPEN_ALWAYS + && unsafe { c::GetLastError() } == c::ERROR_ALREADY_EXISTS + { + unsafe { + // This originally used `FileAllocationInfo` instead of + // `FileEndOfFileInfo` but that wasn't supported by WINE. + // It's arguable which fits the semantics of `OpenOptions` + // better so let's just use the more widely supported method. + let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 }; + let result = c::SetFileInformationByHandle( + handle.as_raw_handle(), + c::FileEndOfFileInfo, + ptr::addr_of!(eof).cast::<c_void>(), + mem::size_of::<c::FILE_END_OF_FILE_INFO>() as u32, + ); + if result == 0 { + return Err(io::Error::last_os_error()); + } + } + } Ok(File { handle: Handle::from_inner(handle) }) } else { Err(Error::last_os_error()) @@ -548,7 +571,7 @@ impl File { let user = super::args::from_wide_to_user_path( subst.iter().copied().chain([0]).collect(), )?; - Ok(PathBuf::from(OsString::from_wide(&user.strip_suffix(&[0]).unwrap_or(&user)))) + Ok(PathBuf::from(OsString::from_wide(user.strip_suffix(&[0]).unwrap_or(&user)))) } else { Ok(PathBuf::from(OsString::from_wide(subst))) } @@ -786,7 +809,7 @@ fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result< // tricked into following a symlink. However, it may not be available in // earlier versions of Windows. static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE); - let mut object = c::OBJECT_ATTRIBUTES { + let object = c::OBJECT_ATTRIBUTES { ObjectName: &mut name_str, RootDirectory: parent.as_raw_handle(), Attributes: ATTRIBUTES.load(Ordering::Relaxed), @@ -795,7 +818,7 @@ fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result< let status = c::NtCreateFile( &mut handle, access, - &mut object, + &object, &mut io_status, crate::ptr::null_mut(), 0, @@ -874,7 +897,7 @@ impl fmt::Debug for File { // FIXME(#24570): add more info here (e.g., mode) let mut b = f.debug_struct("File"); b.field("handle", &self.handle.as_raw_handle()); - if let Ok(path) = get_path(&self) { + if let Ok(path) = get_path(self) { b.field("path", &path); } b.finish() @@ -1193,7 +1216,7 @@ pub fn readlink(path: &Path) -> io::Result<PathBuf> { let mut opts = OpenOptions::new(); opts.access_mode(0); opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); - let file = File::open(&path, &opts)?; + let file = File::open(path, &opts)?; file.readlink() } @@ -1407,7 +1430,7 @@ pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>( #[allow(dead_code)] fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { let d = DirBuilder::new(); - d.mkdir(&junction)?; + d.mkdir(junction)?; let mut opts = OpenOptions::new(); opts.write(true); diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs index 56d0d6c08..c4495f81a 100644 --- a/library/std/src/sys/windows/handle.rs +++ b/library/std/src/sys/windows/handle.rs @@ -81,7 +81,7 @@ impl Handle { let res = unsafe { self.synchronous_read(buf.as_mut_ptr().cast(), buf.len(), None) }; match res { - Ok(read) => Ok(read as usize), + Ok(read) => Ok(read), // The special treatment of BrokenPipe is to deal with Windows // pipe semantics, which yields this error when *reading* from @@ -107,7 +107,7 @@ impl Handle { unsafe { self.synchronous_read(buf.as_mut_ptr().cast(), buf.len(), Some(offset)) }; match res { - Ok(read) => Ok(read as usize), + Ok(read) => Ok(read), Err(ref e) if e.raw_os_error() == Some(c::ERROR_HANDLE_EOF as i32) => Ok(0), Err(e) => Err(e), } @@ -121,7 +121,7 @@ impl Handle { Ok(read) => { // Safety: `read` bytes were written to the initialized portion of the buffer unsafe { - cursor.advance(read as usize); + cursor.advance(read); } Ok(()) } @@ -189,7 +189,7 @@ impl Handle { } pub fn write(&self, buf: &[u8]) -> io::Result<usize> { - self.synchronous_write(&buf, None) + self.synchronous_write(buf, None) } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { @@ -202,7 +202,7 @@ impl Handle { } pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { - self.synchronous_write(&buf, Some(offset)) + self.synchronous_write(buf, Some(offset)) } pub fn try_clone(&self) -> io::Result<Self> { diff --git a/library/std/src/sys/windows/io.rs b/library/std/src/sys/windows/io.rs index 9b540ee07..649826d25 100644 --- a/library/std/src/sys/windows/io.rs +++ b/library/std/src/sys/windows/io.rs @@ -36,7 +36,7 @@ impl<'a> IoSlice<'a> { #[inline] pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf as *mut u8, self.vec.len as usize) } + unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } } } @@ -70,12 +70,12 @@ impl<'a> IoSliceMut<'a> { #[inline] pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf as *mut u8, self.vec.len as usize) } + unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } } #[inline] pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.len as usize) } + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } } } diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index c4e56e13b..8b722f01a 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -1,6 +1,6 @@ #![allow(missing_docs, nonstandard_style)] -use crate::ffi::{CStr, OsStr, OsString}; +use crate::ffi::{OsStr, OsString}; use crate::io::ErrorKind; use crate::mem::MaybeUninit; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; @@ -63,7 +63,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already // exists, we have to call it ourselves. - thread::Thread::set_name(&CStr::from_bytes_with_nul_unchecked(b"main\0")); + thread::Thread::set_name(&c"main"); } // SAFETY: must be called only once during runtime cleanup. @@ -150,7 +150,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option<usize> { let ptr = haystack.as_ptr(); - let mut start = &haystack[..]; + let mut start = haystack; // For performance reasons unfold the loop eight times. while start.len() >= 8 { diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index c29b86366..6cd758ec5 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -162,7 +162,7 @@ impl Socket { let mut timeout = c::timeval { tv_sec: cmp::min(timeout.as_secs(), c_long::MAX as u64) as c_long, - tv_usec: (timeout.subsec_nanos() / 1000) as c_long, + tv_usec: timeout.subsec_micros() as c_long, }; if timeout.tv_sec == 0 && timeout.tv_usec == 0 { diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs index 8cc905101..829dd5eb9 100644 --- a/library/std/src/sys/windows/os.rs +++ b/library/std/src/sys/windows/os.rs @@ -297,7 +297,7 @@ pub fn getenv(k: &OsStr) -> Option<OsString> { let k = to_u16s(k).ok()?; super::fill_utf16_buf( |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) }, - |buf| OsStringExt::from_wide(buf), + OsStringExt::from_wide, ) .ok() } @@ -356,7 +356,7 @@ pub fn home_dir() -> Option<PathBuf> { crate::env::var_os("HOME") .or_else(|| crate::env::var_os("USERPROFILE")) .map(PathBuf::from) - .or_else(|| home_dir_crt()) + .or_else(home_dir_crt) } pub fn exit(code: i32) -> ! { @@ -364,5 +364,5 @@ pub fn exit(code: i32) -> ! { } pub fn getpid() -> u32 { - unsafe { c::GetCurrentProcessId() as u32 } + unsafe { c::GetCurrentProcessId() } } diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs index 8c0e07b35..d9684f217 100644 --- a/library/std/src/sys/windows/path.rs +++ b/library/std/src/sys/windows/path.rs @@ -78,7 +78,7 @@ impl<'a> PrefixParserSlice<'a, '_> { fn strip_prefix(&self, prefix: &str) -> Option<Self> { self.prefix[self.index..] .starts_with(prefix.as_bytes()) - .then(|| Self { index: self.index + prefix.len(), ..*self }) + .then_some(Self { index: self.index + prefix.len(), ..*self }) } fn prefix_bytes(&self) -> &'a [u8] { @@ -104,7 +104,9 @@ pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> { // The meaning of verbatim paths can change when they use a different // separator. - if let Some(parser) = parser.strip_prefix(r"?\") && !parser.prefix_bytes().iter().any(|&x| x == b'/') { + if let Some(parser) = parser.strip_prefix(r"?\") + && !parser.prefix_bytes().iter().any(|&x| x == b'/') + { // \\?\ if let Some(parser) = parser.strip_prefix(r"UNC\") { // \\?\UNC\server\share @@ -145,12 +147,10 @@ pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> { None } } - } else if let Some(drive) = parse_drive(path) { - // C: - Some(Disk(drive)) } else { - // no prefix - None + // If it has a drive like `C:` then it's a disk. + // Otherwise there is no prefix. + parse_drive(path).map(Disk) } } @@ -250,7 +250,7 @@ pub(crate) fn get_long_path(mut path: Vec<u16>, prefer_verbatim: bool) -> io::Re // \\?\UNC\ const UNC_PREFIX: &[u16] = &[SEP, SEP, QUERY, SEP, U, N, C, SEP]; - if path.starts_with(VERBATIM_PREFIX) || path.starts_with(NT_PREFIX) || path == &[0] { + if path.starts_with(VERBATIM_PREFIX) || path.starts_with(NT_PREFIX) || path == [0] { // Early return for paths that are already verbatim or empty. return Ok(path); } else if path.len() < LEGACY_MAX_PATH { diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index f4078d359..9ec775959 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -245,7 +245,7 @@ impl Command { } pub fn get_current_dir(&self) -> Option<&Path> { - self.cwd.as_ref().map(|cwd| Path::new(cwd)) + self.cwd.as_ref().map(Path::new) } pub unsafe fn raw_attribute<T: Copy + Send + Sync + 'static>( @@ -463,7 +463,7 @@ fn resolve_exe<'a>( // Search the directories given by `search_paths`. let result = search_paths(parent_paths, child_paths, |mut path| { - path.push(&exe_path); + path.push(exe_path); if !has_extension { path.set_extension(EXE_EXTENSION); } @@ -597,7 +597,7 @@ impl Stdio { opts.read(stdio_id == c::STD_INPUT_HANDLE); opts.write(stdio_id != c::STD_INPUT_HANDLE); opts.security_attributes(&mut sa); - File::open(Path::new("NUL"), &opts).map(|file| file.into_inner()) + File::open(Path::new(r"\\.\NUL"), &opts).map(|file| file.into_inner()) } } } @@ -657,7 +657,7 @@ impl Process { } pub fn id(&self) -> u32 { - unsafe { c::GetProcessId(self.handle.as_raw_handle()) as u32 } + unsafe { c::GetProcessId(self.handle.as_raw_handle()) } } pub fn main_thread_handle(&self) -> BorrowedHandle<'_> { @@ -917,9 +917,8 @@ fn make_proc_thread_attribute_list( ) }; - let mut proc_thread_attribute_list = ProcThreadAttributeList( - vec![MaybeUninit::uninit(); required_size as usize].into_boxed_slice(), - ); + let mut proc_thread_attribute_list = + ProcThreadAttributeList(vec![MaybeUninit::uninit(); required_size].into_boxed_slice()); // Once we've allocated the necessary memory, it's safe to invoke // `InitializeProcThreadAttributeList` to properly initialize the list. diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index a9ff909aa..819a48266 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -195,7 +195,7 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usiz MaybeUninit::slice_assume_init_ref(&utf16[..result as usize]) }; - let mut written = write_u16s(handle, &utf16)?; + let mut written = write_u16s(handle, utf16)?; // Figure out how many bytes of as UTF-8 were written away as UTF-16. if written == utf16.len() { @@ -207,7 +207,7 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usiz // write the missing surrogate out now. // Buffering it would mean we have to lie about the number of bytes written. let first_code_unit_remaining = utf16[written]; - if first_code_unit_remaining >= 0xDCEE && first_code_unit_remaining <= 0xDFFF { + if matches!(first_code_unit_remaining, 0xDCEE..=0xDFFF) { // low surrogate // We just hope this works, and give up otherwise let _ = write_u16s(handle, &utf16[written..written + 1]); @@ -266,7 +266,7 @@ impl io::Read for Stdin { let mut bytes_copied = self.incomplete_utf8.read(buf); if bytes_copied == buf.len() { - return Ok(bytes_copied); + Ok(bytes_copied) } else if buf.len() - bytes_copied < 4 { // Not enough space to get a UTF-8 byte. We will use the incomplete UTF8. let mut utf16_buf = [MaybeUninit::new(0); 1]; @@ -332,7 +332,7 @@ fn read_u16s_fixup_surrogates( // and it is not 0, so we know that `buf[amount - 1]` have been // initialized. let last_char = unsafe { buf[amount - 1].assume_init() }; - if last_char >= 0xD800 && last_char <= 0xDBFF { + if matches!(last_char, 0xD800..=0xDBFF) { // high surrogate *surrogate = last_char; amount -= 1; diff --git a/library/std/src/sys/windows/time.rs b/library/std/src/sys/windows/time.rs index bece48e79..09e78a293 100644 --- a/library/std/src/sys/windows/time.rs +++ b/library/std/src/sys/windows/time.rs @@ -1,7 +1,7 @@ use crate::cmp::Ordering; use crate::fmt; use crate::mem; -use crate::ptr::{null, null_mut}; +use crate::ptr::null; use crate::sys::c; use crate::sys_common::IntoInner; use crate::time::Duration; @@ -240,7 +240,7 @@ impl WaitableTimer { c::TIMER_ALL_ACCESS, ) }; - if handle != null_mut() { Ok(Self { handle }) } else { Err(()) } + if !handle.is_null() { Ok(Self { handle }) } else { Err(()) } } pub fn set(&self, duration: Duration) -> Result<(), ()> { // Convert the Duration to a format similar to FILETIME. diff --git a/library/std/src/sys/xous/mod.rs b/library/std/src/sys/xous/mod.rs index 6d5c218d1..c2550dcfd 100644 --- a/library/std/src/sys/xous/mod.rs +++ b/library/std/src/sys/xous/mod.rs @@ -28,7 +28,6 @@ pub mod process; pub mod stdio; pub mod thread; pub mod thread_local_key; -#[path = "../unsupported/thread_parking.rs"] pub mod thread_parking; pub mod time; diff --git a/library/std/src/sys/xous/os.rs b/library/std/src/sys/xous/os.rs index 3d19fa4b3..8d2eaee8a 100644 --- a/library/std/src/sys/xous/os.rs +++ b/library/std/src/sys/xous/os.rs @@ -8,6 +8,28 @@ use crate::os::xous::ffi::Error as XousError; use crate::path::{self, PathBuf}; #[cfg(not(test))] +#[cfg(feature = "panic_unwind")] +mod eh_unwinding { + pub(crate) struct EhFrameFinder(usize /* eh_frame */); + pub(crate) static mut EH_FRAME_SETTINGS: EhFrameFinder = EhFrameFinder(0); + impl EhFrameFinder { + pub(crate) unsafe fn init(&mut self, eh_frame: usize) { + unsafe { + EH_FRAME_SETTINGS.0 = eh_frame; + } + } + } + unsafe impl unwind::EhFrameFinder for EhFrameFinder { + fn find(&self, _pc: usize) -> Option<unwind::FrameInfo> { + Some(unwind::FrameInfo { + text_base: None, + kind: unwind::FrameInfoKind::EhFrame(self.0), + }) + } + } +} + +#[cfg(not(test))] mod c_compat { use crate::os::xous::ffi::exit; extern "C" { @@ -20,7 +42,12 @@ mod c_compat { } #[no_mangle] - pub extern "C" fn _start() { + pub extern "C" fn _start(eh_frame: usize) { + #[cfg(feature = "panic_unwind")] + unsafe { + super::eh_unwinding::EH_FRAME_SETTINGS.init(eh_frame); + unwind::set_custom_eh_frame_finder(&super::eh_unwinding::EH_FRAME_SETTINGS).ok(); + } exit(unsafe { main() }); } diff --git a/library/std/src/sys/xous/thread_parking.rs b/library/std/src/sys/xous/thread_parking.rs new file mode 100644 index 000000000..aa39c6d27 --- /dev/null +++ b/library/std/src/sys/xous/thread_parking.rs @@ -0,0 +1,94 @@ +use crate::os::xous::ffi::{blocking_scalar, scalar}; +use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; +use crate::pin::Pin; +use crate::ptr; +use crate::sync::atomic::{ + AtomicI8, + Ordering::{Acquire, Release}, +}; +use crate::time::Duration; + +const NOTIFIED: i8 = 1; +const EMPTY: i8 = 0; +const PARKED: i8 = -1; + +pub struct Parker { + state: AtomicI8, +} + +impl Parker { + pub unsafe fn new_in_place(parker: *mut Parker) { + unsafe { parker.write(Parker { state: AtomicI8::new(EMPTY) }) } + } + + fn index(&self) -> usize { + ptr::from_ref(self).addr() + } + + pub unsafe fn park(self: Pin<&Self>) { + // Change NOTIFIED to EMPTY and EMPTY to PARKED. + let state = self.state.fetch_sub(1, Acquire); + if state == NOTIFIED { + return; + } + + // The state was set to PARKED. Wait until the `unpark` wakes us up. + blocking_scalar( + ticktimer_server(), + TicktimerScalar::WaitForCondition(self.index(), 0).into(), + ) + .expect("failed to send WaitForCondition command"); + + self.state.swap(EMPTY, Acquire); + } + + pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) { + // Change NOTIFIED to EMPTY and EMPTY to PARKED. + let state = self.state.fetch_sub(1, Acquire); + if state == NOTIFIED { + return; + } + + // A value of zero indicates an indefinite wait. Clamp the number of + // milliseconds to the allowed range. + let millis = usize::max(timeout.as_millis().try_into().unwrap_or(usize::MAX), 1); + + let was_timeout = blocking_scalar( + ticktimer_server(), + TicktimerScalar::WaitForCondition(self.index(), millis).into(), + ) + .expect("failed to send WaitForCondition command")[0] + != 0; + + let state = self.state.swap(EMPTY, Acquire); + if was_timeout && state == NOTIFIED { + // The state was set to NOTIFIED after we returned from the wait + // but before we reset the state. Therefore, a wakeup is on its + // way, which we need to consume here. + // NOTICE: this is a priority hole. + blocking_scalar( + ticktimer_server(), + TicktimerScalar::WaitForCondition(self.index(), 0).into(), + ) + .expect("failed to send WaitForCondition command"); + } + } + + pub fn unpark(self: Pin<&Self>) { + let state = self.state.swap(NOTIFIED, Release); + if state == PARKED { + // The thread is parked, wake it up. + blocking_scalar( + ticktimer_server(), + TicktimerScalar::NotifyCondition(self.index(), 1).into(), + ) + .expect("failed to send NotifyCondition command"); + } + } +} + +impl Drop for Parker { + fn drop(&mut self) { + scalar(ticktimer_server(), TicktimerScalar::FreeCondition(self.index()).into()).ok(); + } +} diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs index 84e2c5d8d..adfe721cf 100644 --- a/library/std/src/sys_common/backtrace.rs +++ b/library/std/src/sys_common/backtrace.rs @@ -64,6 +64,7 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: let mut first_omit = true; // Start immediately if we're not using a short backtrace. let mut start = print_fmt != PrintFmt::Short; + set_image_base(); backtrace_rs::trace_unsynchronized(|frame| { if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES { return false; @@ -213,3 +214,14 @@ pub fn output_filename( } fmt::Display::fmt(&file.display(), fmt) } + +#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] +pub fn set_image_base() { + let image_base = crate::os::fortanix_sgx::mem::image_base(); + backtrace_rs::set_image_base(crate::ptr::invalid_mut(image_base as _)); +} + +#[cfg(not(all(target_vendor = "fortanix", target_env = "sgx")))] +pub fn set_image_base() { + // nothing to do for platforms other than SGX +} diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index e18638f2a..851832a37 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -43,15 +43,15 @@ cfg_if::cfg_if! { } cfg_if::cfg_if! { - if #[cfg(any(target_os = "l4re", - target_os = "uefi", - feature = "restricted-std", - all(target_family = "wasm", not(target_os = "emscripten")), - target_os = "xous", - all(target_vendor = "fortanix", target_env = "sgx")))] { - pub use crate::sys::net; - } else { + if #[cfg(any( + all(unix, not(target_os = "l4re")), + windows, + target_os = "hermit", + target_os = "solid_asp3" + ))] { pub mod net; + } else { + pub use crate::sys::net; } } diff --git a/library/std/src/sys_common/once/futex.rs b/library/std/src/sys_common/once/futex.rs index 42db5fad4..609085dcd 100644 --- a/library/std/src/sys_common/once/futex.rs +++ b/library/std/src/sys_common/once/futex.rs @@ -128,7 +128,8 @@ impl Once { RUNNING | QUEUED => { // Set the state to QUEUED if it is not already. if state == RUNNING - && let Err(new) = self.state.compare_exchange_weak(RUNNING, QUEUED, Relaxed, Acquire) + && let Err(new) = + self.state.compare_exchange_weak(RUNNING, QUEUED, Relaxed, Acquire) { state = new; continue; diff --git a/library/std/src/sys_common/thread.rs b/library/std/src/sys_common/thread.rs index 76466b2b3..8f5624bbc 100644 --- a/library/std/src/sys_common/thread.rs +++ b/library/std/src/sys_common/thread.rs @@ -8,7 +8,7 @@ pub fn min_stack() -> usize { 0 => {} n => return n - 1, } - let amt = env::var("RUST_MIN_STACK").ok().and_then(|s| s.parse().ok()); + let amt = env::var_os("RUST_MIN_STACK").and_then(|s| s.to_str().and_then(|s| s.parse().ok())); let amt = amt.unwrap_or(imp::DEFAULT_MIN_STACK_SIZE); // 0 is our sentinel value, so ensure that we'll never see 0 after diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs index a07bbe6d7..28a426648 100644 --- a/library/std/src/sys_common/wtf8/tests.rs +++ b/library/std/src/sys_common/wtf8/tests.rs @@ -1,5 +1,4 @@ use super::*; -use crate::borrow::Cow; #[test] fn code_point_from_u32() { diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 4097eb554..849893780 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -548,10 +548,6 @@ impl Builder { 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 { @@ -1585,6 +1581,7 @@ impl<'scope, T> JoinInner<'scope, T> { /// [`thread::Builder::spawn`]: Builder::spawn /// [`thread::spawn`]: spawn #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(target_os = "teeos", must_use)] pub struct JoinHandle<T>(JoinInner<'static, T>); #[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] diff --git a/library/std/tests/common/mod.rs b/library/std/tests/common/mod.rs index 358c2c3f9..1aad6549e 100644 --- a/library/std/tests/common/mod.rs +++ b/library/std/tests/common/mod.rs @@ -1,17 +1,17 @@ #![allow(unused)] +use rand::RngCore; use std::env; use std::fs; use std::path::{Path, PathBuf}; use std::thread; -use rand::RngCore; /// Copied from `std::test_helpers::test_rng`, since these tests rely on the /// seed not being the same for every RNG invocation too. #[track_caller] pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { use core::hash::{BuildHasher, Hash, Hasher}; - let mut hasher = std::collections::hash_map::RandomState::new().build_hasher(); + let mut hasher = std::hash::RandomState::new().build_hasher(); core::panic::Location::caller().hash(&mut hasher); let hc64 = hasher.finish(); let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::<Vec<u8>>(); |