Skip to content

Commit e450511

Browse files
committed
Auto merge of rust-lang#115159 - solid-rs:patch/kmc-solid/io-safety, r=workingjubilee
kmc-solid: I/O safety Adds the I/O safety API (rust-lang#87329) for socket file descriptors in [`*-kmc-solid_*`](https://doc.rust-lang.org/nightly/rustc/platform-support/kmc-solid.html) Tier 3 targets. All new public items are gated by the `solid_ext` library feature. This PR adds the following public types and traits: std::os::solid::io::AsFd std::os::solid::io::BorrowedFd std::os::solid::io::OwnedFd std::os::solid::prelude::AsFd (re-export) std::os::solid::prelude::BorrowedFd (re-export) std::os::solid::prelude::OwnedFd (re-export) And trait implementations: From<std::net::TcpListener> for std::os::solid::io::OwnedFd From<std::net::TcpStream> for std::os::solid::io::OwnedFd From<std::net::UdpSocket> for std::os::solid::io::OwnedFd From<std::os::solid::io::OwnedFd> for std::net::TcpListener From<std::os::solid::io::OwnedFd> for std::net::TcpStream From<std::os::solid::io::OwnedFd> for std::net::UdpSocket std::fmt::Debug for std::os::solid::io::BorrowedFd<'_> std::fmt::Debug for std::os::solid::io::OwnedFd std::io::IsTerminal for std::os::solid::io::BorrowedFd<'_> std::io::IsTerminal for std::os::solid::io::OwnedFd std::os::fd::AsRawFd for std::os::solid::io::BorrowedFd<'_> std::os::fd::AsRawFd for std::os::solid::io::OwnedFd std::os::fd::FromRawFd for std::os::solid::io::OwnedFd std::os::fd::IntoRawFd for std::os::solid::io::OwnedFd std::os::solid::io::AsFd for &impl std::os::solid::io::AsFd std::os::solid::io::AsFd for &mut impl std::os::solid::io::AsFd std::os::solid::io::AsFd for Arc<impl std::os::solid::io::AsFd> std::os::solid::io::AsFd for Box<impl std::os::solid::io::AsFd> std::os::solid::io::AsFd for Rc<impl std::os::solid::io::AsFd> std::os::solid::io::AsFd for std::net::TcpListener std::os::solid::io::AsFd for std::net::TcpStream std::os::solid::io::AsFd for std::net::UdpSocket std::os::solid::io::AsFd for std::os::solid::io::BorrowedFd<'_> std::os::solid::io::AsFd for std::os::solid::io::OwnedFd Taking advantage of the above change, this PR also refactors the internal details of `std::sys::solid::net` to match the design of other targets, e.g., by redefining `Socket` as a newtype of `OwnedFd`.
2 parents 1e9dda7 + 52eb92d commit e450511

File tree

3 files changed

+371
-132
lines changed

3 files changed

+371
-132
lines changed

library/std/src/os/solid/io.rs

+297-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,309 @@
11
//! SOLID-specific extensions to general I/O primitives
2+
//!
3+
//! Just like raw pointers, raw SOLID Sockets file descriptors point to
4+
//! resources with dynamic lifetimes, and they can dangle if they outlive their
5+
//! resources or be forged if they're created from invalid values.
6+
//!
7+
//! This module provides three types for representing raw file descriptors
8+
//! with different ownership properties: raw, borrowed, and owned, which are
9+
//! analogous to types used for representing pointers:
10+
//!
11+
//! | Type | Analogous to |
12+
//! | ------------------ | ------------ |
13+
//! | [`RawFd`] | `*const _` |
14+
//! | [`BorrowedFd<'a>`] | `&'a _` |
15+
//! | [`OwnedFd`] | `Box<_>` |
16+
//!
17+
//! Like raw pointers, `RawFd` values are primitive values. And in new code,
18+
//! they should be considered unsafe to do I/O on (analogous to dereferencing
19+
//! them). Rust did not always provide this guidance, so existing code in the
20+
//! Rust ecosystem often doesn't mark `RawFd` usage as unsafe. Once the
21+
//! `io_safety` feature is stable, libraries will be encouraged to migrate,
22+
//! either by adding `unsafe` to APIs that dereference `RawFd` values, or by
23+
//! using to `BorrowedFd` or `OwnedFd` instead.
24+
//!
25+
//! Like references, `BorrowedFd` values are tied to a lifetime, to ensure
26+
//! that they don't outlive the resource they point to. These are safe to
27+
//! use. `BorrowedFd` values may be used in APIs which provide safe access to
28+
//! any system call except for:
29+
//!
30+
//! - `close`, because that would end the dynamic lifetime of the resource
31+
//! without ending the lifetime of the file descriptor.
32+
//!
33+
//! - `dup2`/`dup3`, in the second argument, because this argument is
34+
//! closed and assigned a new resource, which may break the assumptions
35+
//! other code using that file descriptor.
36+
//!
37+
//! `BorrowedFd` values may be used in APIs which provide safe access to `dup`
38+
//! system calls, so types implementing `AsFd` or `From<OwnedFd>` should not
39+
//! assume they always have exclusive access to the underlying file
40+
//! description.
41+
//!
42+
//! Like boxes, `OwnedFd` values conceptually own the resource they point to,
43+
//! and free (close) it when they are dropped.
44+
//!
45+
//! [`BorrowedFd<'a>`]: crate::os::solid::io::BorrowedFd
246
347
#![deny(unsafe_op_in_unsafe_fn)]
448
#![unstable(feature = "solid_ext", issue = "none")]
549

50+
use crate::fmt;
51+
use crate::marker::PhantomData;
52+
use crate::mem::forget;
653
use crate::net;
754
use crate::sys;
855
use crate::sys_common::{self, AsInner, FromInner, IntoInner};
956

1057
/// Raw file descriptors.
1158
pub type RawFd = i32;
1259

60+
/// A borrowed SOLID Sockets file descriptor.
61+
///
62+
/// This has a lifetime parameter to tie it to the lifetime of something that
63+
/// owns the socket.
64+
///
65+
/// This uses `repr(transparent)` and has the representation of a host file
66+
/// descriptor, so it can be used in FFI in places where a socket is passed as
67+
/// an argument, it is not captured or consumed, and it never has the value
68+
/// `SOLID_NET_INVALID_FD`.
69+
///
70+
/// This type's `.to_owned()` implementation returns another `BorrowedFd`
71+
/// rather than an `OwnedFd`. It just makes a trivial copy of the raw
72+
/// socket, which is then borrowed under the same lifetime.
73+
#[derive(Copy, Clone)]
74+
#[repr(transparent)]
75+
#[rustc_layout_scalar_valid_range_start(0)]
76+
// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`.
77+
#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
78+
#[rustc_nonnull_optimization_guaranteed]
79+
pub struct BorrowedFd<'socket> {
80+
fd: RawFd,
81+
_phantom: PhantomData<&'socket OwnedFd>,
82+
}
83+
84+
/// An owned SOLID Sockets file descriptor.
85+
///
86+
/// This closes the file descriptor on drop.
87+
///
88+
/// This uses `repr(transparent)` and has the representation of a host file
89+
/// descriptor, so it can be used in FFI in places where a socket is passed as
90+
/// an argument, it is not captured or consumed, and it never has the value
91+
/// `SOLID_NET_INVALID_FD`.
92+
#[repr(transparent)]
93+
#[rustc_layout_scalar_valid_range_start(0)]
94+
// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`.
95+
#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
96+
#[rustc_nonnull_optimization_guaranteed]
97+
pub struct OwnedFd {
98+
fd: RawFd,
99+
}
100+
101+
impl BorrowedFd<'_> {
102+
/// Return a `BorrowedFd` holding the given raw file descriptor.
103+
///
104+
/// # Safety
105+
///
106+
/// The resource pointed to by `fd` must remain open for the duration of
107+
/// the returned `BorrowedFd`, and it must not have the value
108+
/// `SOLID_NET_INVALID_FD`.
109+
#[inline]
110+
pub const unsafe fn borrow_raw(fd: RawFd) -> Self {
111+
assert!(fd != -1 as RawFd);
112+
// SAFETY: we just asserted that the value is in the valid range and
113+
// isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
114+
unsafe { Self { fd, _phantom: PhantomData } }
115+
}
116+
}
117+
118+
impl OwnedFd {
119+
/// Creates a new `OwnedFd` instance that shares the same underlying file
120+
/// description as the existing `OwnedFd` instance.
121+
pub fn try_clone(&self) -> crate::io::Result<Self> {
122+
self.as_fd().try_clone_to_owned()
123+
}
124+
}
125+
126+
impl BorrowedFd<'_> {
127+
/// Creates a new `OwnedFd` instance that shares the same underlying file
128+
/// description as the existing `BorrowedFd` instance.
129+
pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> {
130+
let fd = sys::net::cvt(unsafe { sys::net::netc::dup(self.as_raw_fd()) })?;
131+
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
132+
}
133+
}
134+
135+
impl AsRawFd for BorrowedFd<'_> {
136+
#[inline]
137+
fn as_raw_fd(&self) -> RawFd {
138+
self.fd
139+
}
140+
}
141+
142+
impl AsRawFd for OwnedFd {
143+
#[inline]
144+
fn as_raw_fd(&self) -> RawFd {
145+
self.fd
146+
}
147+
}
148+
149+
impl IntoRawFd for OwnedFd {
150+
#[inline]
151+
fn into_raw_fd(self) -> RawFd {
152+
let fd = self.fd;
153+
forget(self);
154+
fd
155+
}
156+
}
157+
158+
impl FromRawFd for OwnedFd {
159+
/// Constructs a new instance of `Self` from the given raw file descriptor.
160+
///
161+
/// # Safety
162+
///
163+
/// The resource pointed to by `fd` must be open and suitable for assuming
164+
/// ownership. The resource must not require any cleanup other than `close`.
165+
#[inline]
166+
unsafe fn from_raw_fd(fd: RawFd) -> Self {
167+
assert_ne!(fd, -1 as RawFd);
168+
// SAFETY: we just asserted that the value is in the valid range and
169+
// isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
170+
unsafe { Self { fd } }
171+
}
172+
}
173+
174+
impl Drop for OwnedFd {
175+
#[inline]
176+
fn drop(&mut self) {
177+
unsafe { sys::net::netc::close(self.fd) };
178+
}
179+
}
180+
181+
impl fmt::Debug for BorrowedFd<'_> {
182+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183+
f.debug_struct("BorrowedFd").field("fd", &self.fd).finish()
184+
}
185+
}
186+
187+
impl fmt::Debug for OwnedFd {
188+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189+
f.debug_struct("OwnedFd").field("fd", &self.fd).finish()
190+
}
191+
}
192+
193+
macro_rules! impl_is_terminal {
194+
($($t:ty),*$(,)?) => {$(
195+
#[unstable(feature = "sealed", issue = "none")]
196+
impl crate::sealed::Sealed for $t {}
197+
198+
#[stable(feature = "is_terminal", since = "1.70.0")]
199+
impl crate::io::IsTerminal for $t {
200+
#[inline]
201+
fn is_terminal(&self) -> bool {
202+
crate::sys::io::is_terminal(self)
203+
}
204+
}
205+
)*}
206+
}
207+
208+
impl_is_terminal!(BorrowedFd<'_>, OwnedFd);
209+
210+
/// A trait to borrow the SOLID Sockets file descriptor from an underlying
211+
/// object.
212+
pub trait AsFd {
213+
/// Borrows the file descriptor.
214+
fn as_fd(&self) -> BorrowedFd<'_>;
215+
}
216+
217+
impl<T: AsFd> AsFd for &T {
218+
#[inline]
219+
fn as_fd(&self) -> BorrowedFd<'_> {
220+
T::as_fd(self)
221+
}
222+
}
223+
224+
impl<T: AsFd> AsFd for &mut T {
225+
#[inline]
226+
fn as_fd(&self) -> BorrowedFd<'_> {
227+
T::as_fd(self)
228+
}
229+
}
230+
231+
impl AsFd for BorrowedFd<'_> {
232+
#[inline]
233+
fn as_fd(&self) -> BorrowedFd<'_> {
234+
*self
235+
}
236+
}
237+
238+
impl AsFd for OwnedFd {
239+
#[inline]
240+
fn as_fd(&self) -> BorrowedFd<'_> {
241+
// Safety: `OwnedFd` and `BorrowedFd` have the same validity
242+
// invariants, and the `BorrowedFd` is bounded by the lifetime
243+
// of `&self`.
244+
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
245+
}
246+
}
247+
248+
macro_rules! impl_owned_fd_traits {
249+
($($t:ident)*) => {$(
250+
impl AsFd for net::$t {
251+
#[inline]
252+
fn as_fd(&self) -> BorrowedFd<'_> {
253+
self.as_inner().socket().as_fd()
254+
}
255+
}
256+
257+
impl From<net::$t> for OwnedFd {
258+
#[inline]
259+
fn from(socket: net::$t) -> OwnedFd {
260+
socket.into_inner().into_socket().into_inner()
261+
}
262+
}
263+
264+
impl From<OwnedFd> for net::$t {
265+
#[inline]
266+
fn from(owned_fd: OwnedFd) -> Self {
267+
Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned_fd)))
268+
}
269+
}
270+
)*};
271+
}
272+
impl_owned_fd_traits! { TcpStream TcpListener UdpSocket }
273+
274+
/// This impl allows implementing traits that require `AsFd` on Arc.
275+
/// ```
276+
/// # #[cfg(target_os = "solid_asp3")] mod group_cfg {
277+
/// # use std::os::solid::io::AsFd;
278+
/// use std::net::UdpSocket;
279+
/// use std::sync::Arc;
280+
///
281+
/// trait MyTrait: AsFd {}
282+
/// impl MyTrait for Arc<UdpSocket> {}
283+
/// impl MyTrait for Box<UdpSocket> {}
284+
/// # }
285+
/// ```
286+
impl<T: AsFd> AsFd for crate::sync::Arc<T> {
287+
#[inline]
288+
fn as_fd(&self) -> BorrowedFd<'_> {
289+
(**self).as_fd()
290+
}
291+
}
292+
293+
impl<T: AsFd> AsFd for crate::rc::Rc<T> {
294+
#[inline]
295+
fn as_fd(&self) -> BorrowedFd<'_> {
296+
(**self).as_fd()
297+
}
298+
}
299+
300+
impl<T: AsFd> AsFd for Box<T> {
301+
#[inline]
302+
fn as_fd(&self) -> BorrowedFd<'_> {
303+
(**self).as_fd()
304+
}
305+
}
306+
13307
/// A trait to extract the raw SOLID Sockets file descriptor from an underlying
14308
/// object.
15309
pub trait AsRawFd {
@@ -84,7 +378,7 @@ macro_rules! impl_as_raw_fd {
84378
impl AsRawFd for net::$t {
85379
#[inline]
86380
fn as_raw_fd(&self) -> RawFd {
87-
*self.as_inner().socket().as_inner()
381+
self.as_inner().socket().as_raw_fd()
88382
}
89383
}
90384
)*};
@@ -97,7 +391,7 @@ macro_rules! impl_from_raw_fd {
97391
impl FromRawFd for net::$t {
98392
#[inline]
99393
unsafe fn from_raw_fd(fd: RawFd) -> net::$t {
100-
let socket = sys::net::Socket::from_inner(fd);
394+
let socket = unsafe { sys::net::Socket::from_raw_fd(fd) };
101395
net::$t::from_inner(sys_common::net::$t::from_inner(socket))
102396
}
103397
}
@@ -111,7 +405,7 @@ macro_rules! impl_into_raw_fd {
111405
impl IntoRawFd for net::$t {
112406
#[inline]
113407
fn into_raw_fd(self) -> RawFd {
114-
self.into_inner().into_socket().into_inner()
408+
self.into_inner().into_socket().into_raw_fd()
115409
}
116410
}
117411
)*};

library/std/src/os/solid/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ pub mod prelude {
1313
pub use super::ffi::{OsStrExt, OsStringExt};
1414
#[doc(no_inline)]
1515
#[stable(feature = "rust1", since = "1.0.0")]
16-
pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
16+
pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
1717
}

0 commit comments

Comments
 (0)