about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
authorThe Miri Cronjob Bot <miri@cron.bot>2025-05-25 05:01:19 +0000
committerThe Miri Cronjob Bot <miri@cron.bot>2025-05-25 05:01:19 +0000
commitda39cbec739f2da49fa2b403e4e900c302a2c3de (patch)
tree6e8fd448eae0cd2c457d648f654459b0984c9a60 /library/std/src
parent5c65c35d2626db646faa510481edd4ac59f1bafe (diff)
parent396c5cafe70f735e3d0ea0c8982c6901bcf0023a (diff)
downloadrust-da39cbec739f2da49fa2b403e4e900c302a2c3de.tar.gz
rust-da39cbec739f2da49fa2b403e4e900c302a2c3de.zip
Merge from rustc
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/io/pipe.rs38
-rw-r--r--library/std/src/os/net/linux_ext/addr.rs6
-rw-r--r--library/std/src/os/net/linux_ext/socket.rs3
-rw-r--r--library/std/src/os/net/linux_ext/tcp.rs6
-rw-r--r--library/std/src/panic.rs2
-rw-r--r--library/std/src/panicking.rs8
-rw-r--r--library/std/src/process.rs14
-rw-r--r--library/std/src/sync/mpmc/list.rs2
-rw-r--r--library/std/src/sys/net/connection/uefi/mod.rs59
-rw-r--r--library/std/src/sys/net/connection/uefi/tcp.rs21
-rw-r--r--library/std/src/sys/net/connection/uefi/tcp4.rs118
-rw-r--r--library/std/src/sys/pal/uefi/helpers.rs10
-rw-r--r--library/std/src/sys/pal/windows/os.rs2
-rw-r--r--library/std/src/sys/process/unix/common.rs116
-rw-r--r--library/std/src/sys/process/unix/common/cstring_array.rs115
-rw-r--r--library/std/src/sys/thread_local/guard/key.rs4
16 files changed, 376 insertions, 148 deletions
diff --git a/library/std/src/io/pipe.rs b/library/std/src/io/pipe.rs
index 47243806cd2..16727d44541 100644
--- a/library/std/src/io/pipe.rs
+++ b/library/std/src/io/pipe.rs
@@ -38,30 +38,44 @@ use crate::sys_common::{FromInner, IntoInner};
 /// > not rely on a particular capacity: an application should be designed so that a reading process
 /// > consumes data as soon as it is available, so that a writing process does not remain blocked.
 ///
-/// # Examples
+/// # Example
 ///
 /// ```no_run
 /// # #[cfg(miri)] fn main() {}
 /// # #[cfg(not(miri))]
 /// # fn main() -> std::io::Result<()> {
+/// use std::io::{Read, Write, pipe};
 /// use std::process::Command;
-/// use std::io::{pipe, Read, Write};
-/// let (ping_rx, mut ping_tx) = pipe()?;
-/// let (mut pong_rx, pong_tx) = pipe()?;
+/// let (ping_reader, mut ping_writer) = pipe()?;
+/// let (mut pong_reader, pong_writer) = pipe()?;
 ///
-/// // Spawn a process that echoes its input.
-/// let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?;
+/// // Spawn a child process that echoes its input.
+/// let mut echo_command = Command::new("cat");
+/// echo_command.stdin(ping_reader);
+/// echo_command.stdout(pong_writer);
+/// let mut echo_child = echo_command.spawn()?;
 ///
-/// ping_tx.write_all(b"hello")?;
-/// // Close to unblock echo_server's reader.
-/// drop(ping_tx);
+/// // Send input to the child process. Note that because we're writing all the input before we
+/// // read any output, this could deadlock if the child's input and output pipe buffers both
+/// // filled up. Those buffers are usually at least a few KB, so "hello" is fine, but for longer
+/// // inputs we'd need to read and write at the same time, e.g. using threads.
+/// ping_writer.write_all(b"hello")?;
+///
+/// // `cat` exits when it reads EOF from stdin, but that can't happen while any ping writer
+/// // remains open. We need to drop our ping writer, or read_to_string will deadlock below.
+/// drop(ping_writer);
+///
+/// // The pong reader can't report EOF while any pong writer remains open. Our Command object is
+/// // holding a pong writer, and again read_to_string will deadlock if we don't drop it.
+/// drop(echo_command);
 ///
 /// let mut buf = String::new();
-/// // Block until echo_server's writer is closed.
-/// pong_rx.read_to_string(&mut buf)?;
+/// // Block until `cat` closes its stdout (a pong writer).
+/// pong_reader.read_to_string(&mut buf)?;
 /// assert_eq!(&buf, "hello");
 ///
-/// echo_server.wait()?;
+/// // At this point we know `cat` has exited, but we still need to wait to clean up the "zombie".
+/// echo_child.wait()?;
 /// # Ok(())
 /// # }
 /// ```
diff --git a/library/std/src/os/net/linux_ext/addr.rs b/library/std/src/os/net/linux_ext/addr.rs
index aed772056e1..41009c0e284 100644
--- a/library/std/src/os/net/linux_ext/addr.rs
+++ b/library/std/src/os/net/linux_ext/addr.rs
@@ -23,7 +23,10 @@ pub trait SocketAddrExt: Sealed {
     ///
     /// ```no_run
     /// use std::os::unix::net::{UnixListener, SocketAddr};
+    /// #[cfg(target_os = "linux")]
     /// use std::os::linux::net::SocketAddrExt;
+    /// #[cfg(target_os = "android")]
+    /// use std::os::android::net::SocketAddrExt;
     ///
     /// fn main() -> std::io::Result<()> {
     ///     let addr = SocketAddr::from_abstract_name(b"hidden")?;
@@ -48,7 +51,10 @@ pub trait SocketAddrExt: Sealed {
     ///
     /// ```no_run
     /// use std::os::unix::net::{UnixListener, SocketAddr};
+    /// #[cfg(target_os = "linux")]
     /// use std::os::linux::net::SocketAddrExt;
+    /// #[cfg(target_os = "android")]
+    /// use std::os::android::net::SocketAddrExt;
     ///
     /// fn main() -> std::io::Result<()> {
     ///     let name = b"hidden";
diff --git a/library/std/src/os/net/linux_ext/socket.rs b/library/std/src/os/net/linux_ext/socket.rs
index 4e4168f693c..a15feb6bd9f 100644
--- a/library/std/src/os/net/linux_ext/socket.rs
+++ b/library/std/src/os/net/linux_ext/socket.rs
@@ -27,7 +27,10 @@ pub trait UnixSocketExt: Sealed {
     ///
     /// ```no_run
     /// #![feature(unix_socket_ancillary_data)]
+    /// #[cfg(target_os = "linux")]
     /// use std::os::linux::net::UnixSocketExt;
+    /// #[cfg(target_os = "android")]
+    /// use std::os::android::net::UnixSocketExt;
     /// use std::os::unix::net::UnixDatagram;
     ///
     /// fn main() -> std::io::Result<()> {
diff --git a/library/std/src/os/net/linux_ext/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs
index c8d012962d4..95dffb3bc43 100644
--- a/library/std/src/os/net/linux_ext/tcp.rs
+++ b/library/std/src/os/net/linux_ext/tcp.rs
@@ -25,7 +25,10 @@ pub trait TcpStreamExt: Sealed {
     /// ```no_run
     /// #![feature(tcp_quickack)]
     /// use std::net::TcpStream;
+    /// #[cfg(target_os = "linux")]
     /// use std::os::linux::net::TcpStreamExt;
+    /// #[cfg(target_os = "android")]
+    /// use std::os::android::net::TcpStreamExt;
     ///
     /// let stream = TcpStream::connect("127.0.0.1:8080")
     ///         .expect("Couldn't connect to the server...");
@@ -43,7 +46,10 @@ pub trait TcpStreamExt: Sealed {
     /// ```no_run
     /// #![feature(tcp_quickack)]
     /// use std::net::TcpStream;
+    /// #[cfg(target_os = "linux")]
     /// use std::os::linux::net::TcpStreamExt;
+    /// #[cfg(target_os = "android")]
+    /// use std::os::android::net::TcpStreamExt;
     ///
     /// let stream = TcpStream::connect("127.0.0.1:8080")
     ///         .expect("Couldn't connect to the server...");
diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs
index f3b26ac64df..234fb284a59 100644
--- a/library/std/src/panic.rs
+++ b/library/std/src/panic.rs
@@ -356,7 +356,7 @@ pub use core::panic::abort_unwind;
 /// ```
 #[stable(feature = "catch_unwind", since = "1.9.0")]
 pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
-    unsafe { panicking::r#try(f) }
+    unsafe { panicking::catch_unwind(f) }
 }
 
 /// Triggers a panic without invoking the panic hook.
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 4bfedf78366..7873049d20b 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -499,13 +499,13 @@ pub use realstd::rt::panic_count;
 
 /// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
 #[cfg(feature = "panic_immediate_abort")]
-pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
+pub unsafe fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
     Ok(f())
 }
 
 /// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
 #[cfg(not(feature = "panic_immediate_abort"))]
-pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
+pub unsafe fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
     union Data<F, R> {
         f: ManuallyDrop<F>,
         r: ManuallyDrop<R>,
@@ -541,7 +541,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
     let data_ptr = (&raw mut data) as *mut u8;
     // SAFETY:
     //
-    // Access to the union's fields: this is `std` and we know that the `r#try`
+    // Access to the union's fields: this is `std` and we know that the `catch_unwind`
     // intrinsic fills in the `r` or `p` union field based on its return value.
     //
     // The call to `intrinsics::catch_unwind` is made safe by:
@@ -602,7 +602,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
     // This function cannot be marked as `unsafe` because `intrinsics::catch_unwind`
     // expects normal function pointers.
     #[inline]
-    #[rustc_nounwind] // `intrinsic::r#try` requires catch fn to be nounwind
+    #[rustc_nounwind] // `intrinsic::catch_unwind` requires catch fn to be nounwind
     fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
         // SAFETY: this is the responsibility of the caller, see above.
         //
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index df6b9a6e563..373584d0117 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -1348,7 +1348,7 @@ impl Output {
     ///
     /// ```
     /// #![feature(exit_status_error)]
-    /// # #[cfg(unix)] {
+    /// # #[cfg(all(unix, not(target_os = "android")))] {
     /// use std::process::Command;
     /// assert!(Command::new("false").output().unwrap().exit_ok().is_err());
     /// # }
@@ -1695,7 +1695,7 @@ impl From<io::Stdout> for Stdio {
     /// # Ok(())
     /// # }
     /// #
-    /// # if cfg!(unix) {
+    /// # if cfg!(all(unix, not(target_os = "android"))) {
     /// #     test().unwrap();
     /// # }
     /// ```
@@ -1724,7 +1724,7 @@ impl From<io::Stderr> for Stdio {
     /// # Ok(())
     /// # }
     /// #
-    /// # if cfg!(unix) {
+    /// # if cfg!(all(unix, not(target_os = "android"))) {
     /// #     test().unwrap();
     /// # }
     /// ```
@@ -1907,7 +1907,7 @@ impl crate::sealed::Sealed for ExitStatusError {}
 ///
 /// ```
 /// #![feature(exit_status_error)]
-/// # if cfg!(unix) {
+/// # if cfg!(all(unix, not(target_os = "android"))) {
 /// use std::process::{Command, ExitStatusError};
 ///
 /// fn run(cmd: &str) -> Result<(), ExitStatusError> {
@@ -1950,7 +1950,7 @@ impl ExitStatusError {
     ///
     /// ```
     /// #![feature(exit_status_error)]
-    /// # #[cfg(unix)] {
+    /// # #[cfg(all(unix, not(target_os = "android")))] {
     /// use std::process::Command;
     ///
     /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err();
@@ -1975,7 +1975,7 @@ impl ExitStatusError {
     /// ```
     /// #![feature(exit_status_error)]
     ///
-    /// # if cfg!(unix) {
+    /// # if cfg!(all(unix, not(target_os = "android"))) {
     /// use std::num::NonZero;
     /// use std::process::Command;
     ///
@@ -2532,7 +2532,7 @@ pub fn id() -> u32 {
 #[rustc_on_unimplemented(on(
     cause = "MainFunctionType",
     message = "`main` has invalid return type `{Self}`",
-    label = "`main` can only return types that implement `{Termination}`"
+    label = "`main` can only return types that implement `{This}`"
 ))]
 pub trait Termination {
     /// Is called to get the representation of the value as status code.
diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs
index 3fcfb85cf2a..050f26b097a 100644
--- a/library/std/src/sync/mpmc/list.rs
+++ b/library/std/src/sync/mpmc/list.rs
@@ -575,7 +575,7 @@ impl<T> Channel<T> {
         // After this point `head.block` is not modified again and it will be deallocated if it's
         // non-null. The `Drop` code of the channel, which runs after this function, also attempts
         // to deallocate `head.block` if it's non-null. Therefore this function must maintain the
-        // invariant that if a deallocation of head.block is attemped then it must also be set to
+        // invariant that if a deallocation of head.block is attempted then it must also be set to
         // NULL. Failing to do so will lead to the Drop code attempting a double free. For this
         // reason both reads above do an atomic swap instead of a simple atomic load.
 
diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs
index da217439626..46d67c8e510 100644
--- a/library/std/src/sys/net/connection/uefi/mod.rs
+++ b/library/std/src/sys/net/connection/uefi/mod.rs
@@ -4,11 +4,14 @@ use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
 use crate::sys::unsupported;
 use crate::time::Duration;
 
-pub struct TcpStream(!);
+mod tcp;
+pub(crate) mod tcp4;
+
+pub struct TcpStream(#[expect(dead_code)] tcp::Tcp);
 
 impl TcpStream {
-    pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
-        unsupported()
+    pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
+        tcp::Tcp::connect(addr?).map(Self)
     }
 
     pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
@@ -16,105 +19,105 @@ impl TcpStream {
     }
 
     pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        self.0
+        unsupported()
     }
 
     pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        self.0
+        unsupported()
     }
 
     pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
-        self.0
+        unsupported()
     }
 
     pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
-        self.0
+        unsupported()
     }
 
     pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
-        self.0
+        unsupported()
     }
 
     pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
-        self.0
+        unsupported()
     }
 
     pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> {
-        self.0
+        unsupported()
     }
 
     pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
-        self.0
+        unsupported()
     }
 
     pub fn is_read_vectored(&self) -> bool {
-        self.0
+        false
     }
 
     pub fn write(&self, _: &[u8]) -> io::Result<usize> {
-        self.0
+        unsupported()
     }
 
     pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> {
-        self.0
+        unsupported()
     }
 
     pub fn is_write_vectored(&self) -> bool {
-        self.0
+        false
     }
 
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
-        self.0
+        unsupported()
     }
 
     pub fn socket_addr(&self) -> io::Result<SocketAddr> {
-        self.0
+        unsupported()
     }
 
     pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
-        self.0
+        unsupported()
     }
 
     pub fn duplicate(&self) -> io::Result<TcpStream> {
-        self.0
+        unsupported()
     }
 
     pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
-        self.0
+        unsupported()
     }
 
     pub fn linger(&self) -> io::Result<Option<Duration>> {
-        self.0
+        unsupported()
     }
 
     pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
-        self.0
+        unsupported()
     }
 
     pub fn nodelay(&self) -> io::Result<bool> {
-        self.0
+        unsupported()
     }
 
     pub fn set_ttl(&self, _: u32) -> io::Result<()> {
-        self.0
+        unsupported()
     }
 
     pub fn ttl(&self) -> io::Result<u32> {
-        self.0
+        unsupported()
     }
 
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
-        self.0
+        unsupported()
     }
 
     pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
-        self.0
+        unsupported()
     }
 }
 
 impl fmt::Debug for TcpStream {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0
+        todo!()
     }
 }
 
diff --git a/library/std/src/sys/net/connection/uefi/tcp.rs b/library/std/src/sys/net/connection/uefi/tcp.rs
new file mode 100644
index 00000000000..f87accdc41d
--- /dev/null
+++ b/library/std/src/sys/net/connection/uefi/tcp.rs
@@ -0,0 +1,21 @@
+use super::tcp4;
+use crate::io;
+use crate::net::SocketAddr;
+
+pub(crate) enum Tcp {
+    V4(#[expect(dead_code)] tcp4::Tcp4),
+}
+
+impl Tcp {
+    pub(crate) fn connect(addr: &SocketAddr) -> io::Result<Self> {
+        match addr {
+            SocketAddr::V4(x) => {
+                let temp = tcp4::Tcp4::new()?;
+                temp.configure(true, Some(x), None)?;
+                temp.connect()?;
+                Ok(Tcp::V4(temp))
+            }
+            SocketAddr::V6(_) => todo!(),
+        }
+    }
+}
diff --git a/library/std/src/sys/net/connection/uefi/tcp4.rs b/library/std/src/sys/net/connection/uefi/tcp4.rs
new file mode 100644
index 00000000000..f7ca373b52b
--- /dev/null
+++ b/library/std/src/sys/net/connection/uefi/tcp4.rs
@@ -0,0 +1,118 @@
+use r_efi::efi::{self, Status};
+use r_efi::protocols::tcp4;
+
+use crate::io;
+use crate::net::SocketAddrV4;
+use crate::ptr::NonNull;
+use crate::sync::atomic::{AtomicBool, Ordering};
+use crate::sys::pal::helpers;
+
+const TYPE_OF_SERVICE: u8 = 8;
+const TIME_TO_LIVE: u8 = 255;
+
+pub(crate) struct Tcp4 {
+    protocol: NonNull<tcp4::Protocol>,
+    flag: AtomicBool,
+    #[expect(dead_code)]
+    service_binding: helpers::ServiceProtocol,
+}
+
+const DEFAULT_ADDR: efi::Ipv4Address = efi::Ipv4Address { addr: [0u8; 4] };
+
+impl Tcp4 {
+    pub(crate) fn new() -> io::Result<Self> {
+        let service_binding = helpers::ServiceProtocol::open(tcp4::SERVICE_BINDING_PROTOCOL_GUID)?;
+        let protocol = helpers::open_protocol(service_binding.child_handle(), tcp4::PROTOCOL_GUID)?;
+
+        Ok(Self { service_binding, protocol, flag: AtomicBool::new(false) })
+    }
+
+    pub(crate) fn configure(
+        &self,
+        active: bool,
+        remote_address: Option<&SocketAddrV4>,
+        station_address: Option<&SocketAddrV4>,
+    ) -> io::Result<()> {
+        let protocol = self.protocol.as_ptr();
+
+        let (remote_address, remote_port) = if let Some(x) = remote_address {
+            (helpers::ipv4_to_r_efi(*x.ip()), x.port())
+        } else {
+            (DEFAULT_ADDR, 0)
+        };
+
+        // FIXME: Remove when passive connections with proper subnet handling are added
+        assert!(station_address.is_none());
+        let use_default_address = efi::Boolean::TRUE;
+        let (station_address, station_port) = (DEFAULT_ADDR, 0);
+        let subnet_mask = helpers::ipv4_to_r_efi(crate::net::Ipv4Addr::new(0, 0, 0, 0));
+
+        let mut config_data = tcp4::ConfigData {
+            type_of_service: TYPE_OF_SERVICE,
+            time_to_live: TIME_TO_LIVE,
+            access_point: tcp4::AccessPoint {
+                use_default_address,
+                remote_address,
+                remote_port,
+                active_flag: active.into(),
+                station_address,
+                station_port,
+                subnet_mask,
+            },
+            control_option: crate::ptr::null_mut(),
+        };
+
+        let r = unsafe { ((*protocol).configure)(protocol, &mut config_data) };
+        if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
+    }
+
+    pub(crate) fn connect(&self) -> io::Result<()> {
+        let evt = unsafe { self.create_evt() }?;
+        let completion_token =
+            tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS };
+
+        let protocol = self.protocol.as_ptr();
+        let mut conn_token = tcp4::ConnectionToken { completion_token };
+
+        let r = unsafe { ((*protocol).connect)(protocol, &mut conn_token) };
+        if r.is_error() {
+            return Err(io::Error::from_raw_os_error(r.as_usize()));
+        }
+
+        self.wait_for_flag();
+
+        if completion_token.status.is_error() {
+            Err(io::Error::from_raw_os_error(completion_token.status.as_usize()))
+        } else {
+            Ok(())
+        }
+    }
+
+    unsafe fn create_evt(&self) -> io::Result<helpers::OwnedEvent> {
+        self.flag.store(false, Ordering::Relaxed);
+        helpers::OwnedEvent::new(
+            efi::EVT_NOTIFY_SIGNAL,
+            efi::TPL_CALLBACK,
+            Some(toggle_atomic_flag),
+            Some(unsafe { NonNull::new_unchecked(self.flag.as_ptr().cast()) }),
+        )
+    }
+
+    fn wait_for_flag(&self) {
+        while !self.flag.load(Ordering::Relaxed) {
+            let _ = self.poll();
+        }
+    }
+
+    fn poll(&self) -> io::Result<()> {
+        let protocol = self.protocol.as_ptr();
+        let r = unsafe { ((*protocol).poll)(protocol) };
+
+        if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
+    }
+}
+
+extern "efiapi" fn toggle_atomic_flag(_: r_efi::efi::Event, ctx: *mut crate::ffi::c_void) {
+    let flag = unsafe { AtomicBool::from_ptr(ctx.cast()) };
+    flag.store(true, Ordering::Relaxed);
+}
diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs
index 6ee3e0a8b66..e47263348db 100644
--- a/library/std/src/sys/pal/uefi/helpers.rs
+++ b/library/std/src/sys/pal/uefi/helpers.rs
@@ -653,7 +653,6 @@ pub(crate) struct ServiceProtocol {
 }
 
 impl ServiceProtocol {
-    #[expect(dead_code)]
     pub(crate) fn open(service_guid: r_efi::efi::Guid) -> io::Result<Self> {
         let handles = locate_handles(service_guid)?;
 
@@ -670,7 +669,6 @@ impl ServiceProtocol {
         Err(io::const_error!(io::ErrorKind::NotFound, "no service binding protocol found"))
     }
 
-    #[expect(dead_code)]
     pub(crate) fn child_handle(&self) -> NonNull<crate::ffi::c_void> {
         self.child_handle
     }
@@ -732,6 +730,10 @@ impl OwnedEvent {
         }
     }
 
+    pub(crate) fn as_ptr(&self) -> efi::Event {
+        self.0.as_ptr()
+    }
+
     pub(crate) fn into_raw(self) -> *mut crate::ffi::c_void {
         let r = self.0.as_ptr();
         crate::mem::forget(self);
@@ -755,3 +757,7 @@ impl Drop for OwnedEvent {
         }
     }
 }
+
+pub(crate) const fn ipv4_to_r_efi(addr: crate::net::Ipv4Addr) -> efi::Ipv4Address {
+    efi::Ipv4Address { addr: addr.octets() }
+}
diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs
index 1ebbbec9e91..f331282d2d7 100644
--- a/library/std/src/sys/pal/windows/os.rs
+++ b/library/std/src/sys/pal/windows/os.rs
@@ -202,8 +202,6 @@ fn home_dir_crt() -> Option<PathBuf> {
             |buf, mut sz| {
                 // GetUserProfileDirectoryW does not quite use the usual protocol for
                 // negotiating the buffer size, so we have to translate.
-                // FIXME(#141254): We rely on the *undocumented* property that this function will
-                // always set the size, not just on failure.
                 match c::GetUserProfileDirectoryW(
                     ptr::without_provenance_mut(CURRENT_PROCESS_TOKEN),
                     buf,
diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs
index e205a839005..b6777b76668 100644
--- a/library/std/src/sys/process/unix/common.rs
+++ b/library/std/src/sys/process/unix/common.rs
@@ -1,8 +1,10 @@
 #[cfg(all(test, not(target_os = "emscripten")))]
 mod tests;
 
-use libc::{EXIT_FAILURE, EXIT_SUCCESS, c_char, c_int, gid_t, pid_t, uid_t};
+use libc::{EXIT_FAILURE, EXIT_SUCCESS, c_int, gid_t, pid_t, uid_t};
 
+pub use self::cstring_array::CStringArray;
+use self::cstring_array::CStringIter;
 use crate::collections::BTreeMap;
 use crate::ffi::{CStr, CString, OsStr, OsString};
 use crate::os::unix::prelude::*;
@@ -14,7 +16,9 @@ use crate::sys::fs::OpenOptions;
 use crate::sys::pipe::{self, AnonPipe};
 use crate::sys::process::env::{CommandEnv, CommandEnvs};
 use crate::sys_common::{FromInner, IntoInner};
-use crate::{fmt, io, ptr};
+use crate::{fmt, io};
+
+mod cstring_array;
 
 cfg_if::cfg_if! {
     if #[cfg(target_os = "fuchsia")] {
@@ -77,13 +81,7 @@ cfg_if::cfg_if! {
 
 pub struct Command {
     program: CString,
-    args: Vec<CString>,
-    /// Exactly what will be passed to `execvp`.
-    ///
-    /// First element is a pointer to `program`, followed by pointers to
-    /// `args`, followed by a `null`. Be careful when modifying `program` or
-    /// `args` to properly update this as well.
-    argv: Argv,
+    args: CStringArray,
     env: CommandEnv,
 
     program_kind: ProgramKind,
@@ -102,14 +100,6 @@ pub struct Command {
     pgroup: Option<pid_t>,
 }
 
-// Create a new type for argv, so that we can make it `Send` and `Sync`
-struct Argv(Vec<*const c_char>);
-
-// It is safe to make `Argv` `Send` and `Sync`, because it contains
-// pointers to memory owned by `Command.args`
-unsafe impl Send for Argv {}
-unsafe impl Sync for Argv {}
-
 // passed back to std::process with the pipes connected to the child, if any
 // were requested
 pub struct StdioPipes {
@@ -171,42 +161,17 @@ impl ProgramKind {
 }
 
 impl Command {
-    #[cfg(not(target_os = "linux"))]
     pub fn new(program: &OsStr) -> Command {
         let mut saw_nul = false;
         let program_kind = ProgramKind::new(program.as_ref());
         let program = os2c(program, &mut saw_nul);
+        let mut args = CStringArray::with_capacity(1);
+        args.push(program.clone());
         Command {
-            argv: Argv(vec![program.as_ptr(), ptr::null()]),
-            args: vec![program.clone()],
             program,
-            program_kind,
+            args,
             env: Default::default(),
-            cwd: None,
-            chroot: None,
-            uid: None,
-            gid: None,
-            saw_nul,
-            closures: Vec::new(),
-            groups: None,
-            stdin: None,
-            stdout: None,
-            stderr: None,
-            pgroup: None,
-        }
-    }
-
-    #[cfg(target_os = "linux")]
-    pub fn new(program: &OsStr) -> Command {
-        let mut saw_nul = false;
-        let program_kind = ProgramKind::new(program.as_ref());
-        let program = os2c(program, &mut saw_nul);
-        Command {
-            argv: Argv(vec![program.as_ptr(), ptr::null()]),
-            args: vec![program.clone()],
-            program,
             program_kind,
-            env: Default::default(),
             cwd: None,
             chroot: None,
             uid: None,
@@ -217,6 +182,7 @@ impl Command {
             stdin: None,
             stdout: None,
             stderr: None,
+            #[cfg(target_os = "linux")]
             create_pidfd: false,
             pgroup: None,
         }
@@ -225,20 +191,11 @@ impl Command {
     pub fn set_arg_0(&mut self, arg: &OsStr) {
         // Set a new arg0
         let arg = os2c(arg, &mut self.saw_nul);
-        debug_assert!(self.argv.0.len() > 1);
-        self.argv.0[0] = arg.as_ptr();
-        self.args[0] = arg;
+        self.args.write(0, arg);
     }
 
     pub fn arg(&mut self, arg: &OsStr) {
-        // Overwrite the trailing null pointer in `argv` and then add a new null
-        // pointer.
         let arg = os2c(arg, &mut self.saw_nul);
-        self.argv.0[self.args.len()] = arg.as_ptr();
-        self.argv.0.push(ptr::null());
-
-        // Also make sure we keep track of the owned value to schedule a
-        // destructor for this memory.
         self.args.push(arg);
     }
 
@@ -295,6 +252,8 @@ impl Command {
 
     pub fn get_args(&self) -> CommandArgs<'_> {
         let mut iter = self.args.iter();
+        // argv[0] contains the program name, but we are only interested in the
+        // arguments so skip it.
         iter.next();
         CommandArgs { iter }
     }
@@ -307,12 +266,12 @@ impl Command {
         self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes())))
     }
 
-    pub fn get_argv(&self) -> &Vec<*const c_char> {
-        &self.argv.0
+    pub fn get_argv(&self) -> &CStringArray {
+        &self.args
     }
 
     pub fn get_program_cstr(&self) -> &CStr {
-        &*self.program
+        &self.program
     }
 
     #[allow(dead_code)]
@@ -405,32 +364,6 @@ fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString {
     })
 }
 
-// Helper type to manage ownership of the strings within a C-style array.
-pub struct CStringArray {
-    items: Vec<CString>,
-    ptrs: Vec<*const c_char>,
-}
-
-impl CStringArray {
-    pub fn with_capacity(capacity: usize) -> Self {
-        let mut result = CStringArray {
-            items: Vec::with_capacity(capacity),
-            ptrs: Vec::with_capacity(capacity + 1),
-        };
-        result.ptrs.push(ptr::null());
-        result
-    }
-    pub fn push(&mut self, item: CString) {
-        let l = self.ptrs.len();
-        self.ptrs[l - 1] = item.as_ptr();
-        self.ptrs.push(ptr::null());
-        self.items.push(item);
-    }
-    pub fn as_ptr(&self) -> *const *const c_char {
-        self.ptrs.as_ptr()
-    }
-}
-
 fn construct_envp(env: BTreeMap<OsString, OsString>, saw_nul: &mut bool) -> CStringArray {
     let mut result = CStringArray::with_capacity(env.len());
     for (mut k, v) in env {
@@ -619,14 +552,16 @@ impl fmt::Debug for Command {
                     write!(f, "{}={value:?} ", key.to_string_lossy())?;
                 }
             }
-            if self.program != self.args[0] {
+
+            if *self.program != self.args[0] {
                 write!(f, "[{:?}] ", self.program)?;
             }
-            write!(f, "{:?}", self.args[0])?;
+            write!(f, "{:?}", &self.args[0])?;
 
-            for arg in &self.args[1..] {
+            for arg in self.get_args() {
                 write!(f, " {:?}", arg)?;
             }
+
             Ok(())
         }
     }
@@ -658,14 +593,16 @@ impl From<u8> for ExitCode {
 }
 
 pub struct CommandArgs<'a> {
-    iter: crate::slice::Iter<'a, CString>,
+    iter: CStringIter<'a>,
 }
 
 impl<'a> Iterator for CommandArgs<'a> {
     type Item = &'a OsStr;
+
     fn next(&mut self) -> Option<&'a OsStr> {
-        self.iter.next().map(|cs| OsStr::from_bytes(cs.as_bytes()))
+        self.iter.next().map(|cs| OsStr::from_bytes(cs.to_bytes()))
     }
+
     fn size_hint(&self) -> (usize, Option<usize>) {
         self.iter.size_hint()
     }
@@ -675,6 +612,7 @@ impl<'a> ExactSizeIterator for CommandArgs<'a> {
     fn len(&self) -> usize {
         self.iter.len()
     }
+
     fn is_empty(&self) -> bool {
         self.iter.is_empty()
     }
diff --git a/library/std/src/sys/process/unix/common/cstring_array.rs b/library/std/src/sys/process/unix/common/cstring_array.rs
new file mode 100644
index 00000000000..1c840a85df9
--- /dev/null
+++ b/library/std/src/sys/process/unix/common/cstring_array.rs
@@ -0,0 +1,115 @@
+use crate::ffi::{CStr, CString, c_char};
+use crate::ops::Index;
+use crate::{fmt, mem, ptr};
+
+/// Helper type to manage ownership of the strings within a C-style array.
+///
+/// This type manages an array of C-string pointers terminated by a null
+/// pointer. The pointer to the array (as returned by `as_ptr`) can be used as
+/// a value of `argv` or `environ`.
+pub struct CStringArray {
+    ptrs: Vec<*const c_char>,
+}
+
+impl CStringArray {
+    /// Creates a new `CStringArray` with enough capacity to hold `capacity`
+    /// strings.
+    pub fn with_capacity(capacity: usize) -> Self {
+        let mut result = CStringArray { ptrs: Vec::with_capacity(capacity + 1) };
+        result.ptrs.push(ptr::null());
+        result
+    }
+
+    /// Replace the string at position `index`.
+    pub fn write(&mut self, index: usize, item: CString) {
+        let argc = self.ptrs.len() - 1;
+        let ptr = &mut self.ptrs[..argc][index];
+        let old = mem::replace(ptr, item.into_raw());
+        // SAFETY:
+        // `CStringArray` owns all of its strings, and they were all transformed
+        // into pointers using `CString::into_raw`. Also, this is not the null
+        // pointer since the indexing above would have failed.
+        drop(unsafe { CString::from_raw(old.cast_mut()) });
+    }
+
+    /// Push an additional string to the array.
+    pub fn push(&mut self, item: CString) {
+        let argc = self.ptrs.len() - 1;
+        // Replace the null pointer at the end of the array...
+        self.ptrs[argc] = item.into_raw();
+        // ... and recreate it to restore the data structure invariant.
+        self.ptrs.push(ptr::null());
+    }
+
+    /// Returns a pointer to the C-string array managed by this type.
+    pub fn as_ptr(&self) -> *const *const c_char {
+        self.ptrs.as_ptr()
+    }
+
+    /// Returns an iterator over all `CStr`s contained in this array.
+    pub fn iter(&self) -> CStringIter<'_> {
+        CStringIter { iter: self.ptrs[..self.ptrs.len() - 1].iter() }
+    }
+}
+
+impl Index<usize> for CStringArray {
+    type Output = CStr;
+    fn index(&self, index: usize) -> &CStr {
+        let ptr = self.ptrs[..self.ptrs.len() - 1][index];
+        // SAFETY:
+        // `CStringArray` owns all of its strings. Also, this is not the null
+        // pointer since the indexing above would have failed.
+        unsafe { CStr::from_ptr(ptr) }
+    }
+}
+
+impl fmt::Debug for CStringArray {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_list().entries(self.iter()).finish()
+    }
+}
+
+// SAFETY: `CStringArray` is basically just a `Vec<CString>`
+unsafe impl Send for CStringArray {}
+// SAFETY: `CStringArray` is basically just a `Vec<CString>`
+unsafe impl Sync for CStringArray {}
+
+impl Drop for CStringArray {
+    fn drop(&mut self) {
+        // SAFETY:
+        // `CStringArray` owns all of its strings, and they were all transformed
+        // into pointers using `CString::into_raw`.
+        self.ptrs[..self.ptrs.len() - 1]
+            .iter()
+            .for_each(|&p| drop(unsafe { CString::from_raw(p.cast_mut()) }))
+    }
+}
+
+/// An iterator over all `CStr`s contained in a `CStringArray`.
+#[derive(Clone)]
+pub struct CStringIter<'a> {
+    iter: crate::slice::Iter<'a, *const c_char>,
+}
+
+impl<'a> Iterator for CStringIter<'a> {
+    type Item = &'a CStr;
+    fn next(&mut self) -> Option<&'a CStr> {
+        // SAFETY:
+        // `CStringArray` owns all of its strings. Also, this is not the null
+        // pointer since the last element is excluded when creating `iter`.
+        self.iter.next().map(|&p| unsafe { CStr::from_ptr(p) })
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+}
+
+impl<'a> ExactSizeIterator for CStringIter<'a> {
+    fn len(&self) -> usize {
+        self.iter.len()
+    }
+    fn is_empty(&self) -> bool {
+        self.iter.is_empty()
+    }
+}
diff --git a/library/std/src/sys/thread_local/guard/key.rs b/library/std/src/sys/thread_local/guard/key.rs
index 59581e6f281..f91471419c1 100644
--- a/library/std/src/sys/thread_local/guard/key.rs
+++ b/library/std/src/sys/thread_local/guard/key.rs
@@ -32,7 +32,7 @@ pub fn enable() {
 
 /// On platforms with key-based TLS, the system runs the destructors for us.
 /// We still have to make sure that [`crate::rt::thread_cleanup`] is called,
-/// however. This is done by defering the execution of a TLS destructor to
+/// however. This is done by deferring the execution of a TLS destructor to
 /// the next round of destruction inside the TLS destructors.
 #[cfg(not(target_thread_local))]
 pub fn enable() {
@@ -46,7 +46,7 @@ pub fn enable() {
     unsafe extern "C" fn run(state: *mut u8) {
         if state == DEFER {
             // Make sure that this function is run again in the next round of
-            // TLS destruction. If there is no futher round, there will be leaks,
+            // TLS destruction. If there is no further round, there will be leaks,
             // but that's okay, `thread_cleanup` is not guaranteed to be called.
             unsafe { set(CLEANUP.force(), RUN) }
         } else {