Skip to content

Commit c8a3079

Browse files
committed
Document the unsafety in Socket::from_raw
1 parent fcfa119 commit c8a3079

File tree

4 files changed

+38
-6
lines changed

4 files changed

+38
-6
lines changed

src/socket.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,40 @@ pub struct Socket {
7474
/// Store a `TcpStream` internally to take advantage of its niche optimizations on Unix platforms.
7575
pub(crate) type Inner = std::net::TcpStream;
7676

77-
// The `sys` module must have access to the below three functions.
7877
impl Socket {
78+
/// # Safety
79+
///
80+
/// The caller must ensure `raw` is a valid file descriptor/socket. NOTE:
81+
/// this should really be marked `unsafe`, but this being an internal
82+
/// function, often passed as mapping function, it's makes it very
83+
/// inconvenient to mark it as `unsafe`.
7984
pub(crate) fn from_raw(raw: sys::Socket) -> Socket {
8085
Socket {
81-
inner: sys::socket_from_raw(raw),
86+
inner: unsafe {
87+
// SAFETY: the caller must ensure that `raw` is a valid file
88+
// descriptor, but when it isn't it could return I/O errors, or
89+
// potentially close a fd it doesn't own. All of that isn't
90+
// memory unsafe, so it's not desired but never memory unsafe or
91+
// causes UB.
92+
//
93+
// However there is one exception. We use `TcpStream` to
94+
// represent the `Socket` internally (see `Inner` type),
95+
// `TcpStream` has a layout optimisation that doesn't allow for
96+
// negative file descriptors (as those are always invalid).
97+
// Violating this assumption (fd never negative) causes UB,
98+
// something we don't want. So check for that we have this
99+
// `assert!`.
100+
#[cfg(unix)]
101+
assert!(raw >= 0, "tried to create a `Socket` with an invalid fd");
102+
sys::socket_from_raw(raw)
103+
},
82104
}
83105
}
106+
84107
pub(crate) fn as_raw(&self) -> sys::Socket {
85108
sys::socket_as_raw(&self.inner)
86109
}
110+
87111
pub(crate) fn into_raw(self) -> sys::Socket {
88112
sys::socket_into_raw(self.inner)
89113
}

src/sys/unix.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -524,8 +524,8 @@ impl SockAddr {
524524

525525
pub(crate) type Socket = c_int;
526526

527-
pub(crate) fn socket_from_raw(socket: Socket) -> crate::socket::Inner {
528-
unsafe { crate::socket::Inner::from_raw_fd(socket) }
527+
pub(crate) unsafe fn socket_from_raw(socket: Socket) -> crate::socket::Inner {
528+
crate::socket::Inner::from_raw_fd(socket)
529529
}
530530

531531
pub(crate) fn socket_as_raw(socket: &crate::socket::Inner) -> Socket {

src/sys/windows.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ fn init() {
191191

192192
pub(crate) type Socket = sock::SOCKET;
193193

194-
pub(crate) fn socket_from_raw(socket: Socket) -> crate::socket::Inner {
195-
unsafe { crate::socket::Inner::from_raw_socket(socket as RawSocket) }
194+
pub(crate) unsafe fn socket_from_raw(socket: Socket) -> crate::socket::Inner {
195+
crate::socket::Inner::from_raw_socket(socket as RawSocket)
196196
}
197197

198198
pub(crate) fn socket_as_raw(socket: &crate::socket::Inner) -> Socket {

tests/socket.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,14 @@ fn protocol_fmt_debug() {
124124
}
125125
}
126126

127+
#[test]
128+
#[should_panic = "tried to create a `Socket` with an invalid fd"]
129+
#[cfg(unix)]
130+
fn from_invalid_raw_fd_should_panic() {
131+
use std::os::unix::io::FromRawFd;
132+
let _socket = unsafe { Socket::from_raw_fd(-1) };
133+
}
134+
127135
#[test]
128136
#[cfg(all(unix, feature = "all"))]
129137
fn socket_address_unix() {

0 commit comments

Comments
 (0)