Skip to content

Commit 042433c

Browse files
authored
net: debug_assert on creating a tokio socket from a blocking one (#7166)
See #5595 and #7172. This adds a debug assertion that checks that a supplied underlying std socket is set to nonblocking mode when constructing a tokio socket object from such an object. This only works on unix.
1 parent 0284d1b commit 042433c

File tree

13 files changed

+101
-3
lines changed

13 files changed

+101
-3
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ unexpected_cfgs = { level = "warn", check-cfg = [
2323
'cfg(fuzzing)',
2424
'cfg(loom)',
2525
'cfg(mio_unsupported_force_poll_poll)',
26+
'cfg(tokio_allow_from_blocking_fd)',
2627
'cfg(tokio_internal_mt_counters)',
2728
'cfg(tokio_no_parking_lot)',
2829
'cfg(tokio_no_tuning_tests)',

tokio/src/net/tcp/listener.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::io::{Interest, PollEvented};
22
use crate::net::tcp::TcpStream;
3+
use crate::util::check_socket_for_blocking;
34

45
cfg_not_wasi! {
56
use crate::net::{to_socket_addrs, ToSocketAddrs};
@@ -209,6 +210,10 @@ impl TcpListener {
209210
/// will block the thread, which will cause unexpected behavior.
210211
/// Non-blocking mode can be set using [`set_nonblocking`].
211212
///
213+
/// Passing a listener in blocking mode is always erroneous,
214+
/// and the behavior in that case may change in the future.
215+
/// For example, it could panic.
216+
///
212217
/// [`set_nonblocking`]: std::net::TcpListener::set_nonblocking
213218
///
214219
/// # Examples
@@ -236,6 +241,8 @@ impl TcpListener {
236241
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
237242
#[track_caller]
238243
pub fn from_std(listener: net::TcpListener) -> io::Result<TcpListener> {
244+
check_socket_for_blocking(&listener)?;
245+
239246
let io = mio::net::TcpListener::from_std(listener);
240247
let io = PollEvented::new(io)?;
241248
Ok(TcpListener { io })

tokio/src/net/tcp/stream.rs

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ cfg_not_wasi! {
77
use crate::io::{AsyncRead, AsyncWrite, Interest, PollEvented, ReadBuf, Ready};
88
use crate::net::tcp::split::{split, ReadHalf, WriteHalf};
99
use crate::net::tcp::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf};
10+
use crate::util::check_socket_for_blocking;
1011

1112
use std::fmt;
1213
use std::io;
@@ -173,6 +174,10 @@ impl TcpStream {
173174
/// will block the thread, which will cause unexpected behavior.
174175
/// Non-blocking mode can be set using [`set_nonblocking`].
175176
///
177+
/// Passing a listener in blocking mode is always erroneous,
178+
/// and the behavior in that case may change in the future.
179+
/// For example, it could panic.
180+
///
176181
/// [`set_nonblocking`]: std::net::TcpStream::set_nonblocking
177182
///
178183
/// # Examples
@@ -200,6 +205,8 @@ impl TcpStream {
200205
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
201206
#[track_caller]
202207
pub fn from_std(stream: std::net::TcpStream) -> io::Result<TcpStream> {
208+
check_socket_for_blocking(&stream)?;
209+
203210
let io = mio::net::TcpStream::from_std(stream);
204211
let io = PollEvented::new(io)?;
205212
Ok(TcpStream { io })

tokio/src/net/udp.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::io::{Interest, PollEvented, ReadBuf, Ready};
22
use crate::net::{to_socket_addrs, ToSocketAddrs};
3+
use crate::util::check_socket_for_blocking;
34

45
use std::fmt;
56
use std::io;
@@ -192,6 +193,10 @@ impl UdpSocket {
192193
/// will block the thread, which will cause unexpected behavior.
193194
/// Non-blocking mode can be set using [`set_nonblocking`].
194195
///
196+
/// Passing a listener in blocking mode is always erroneous,
197+
/// and the behavior in that case may change in the future.
198+
/// For example, it could panic.
199+
///
195200
/// [`set_nonblocking`]: std::net::UdpSocket::set_nonblocking
196201
///
197202
/// # Panics
@@ -220,6 +225,8 @@ impl UdpSocket {
220225
/// ```
221226
#[track_caller]
222227
pub fn from_std(socket: net::UdpSocket) -> io::Result<UdpSocket> {
228+
check_socket_for_blocking(&socket)?;
229+
223230
let io = mio::net::UdpSocket::from_std(socket);
224231
UdpSocket::new(io)
225232
}

tokio/src/net/unix/datagram/socket.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::io::{Interest, PollEvented, ReadBuf, Ready};
22
use crate::net::unix::SocketAddr;
3+
use crate::util::check_socket_for_blocking;
34

45
use std::fmt;
56
use std::io;
@@ -449,6 +450,10 @@ impl UnixDatagram {
449450
/// will block the thread, which will cause unexpected behavior.
450451
/// Non-blocking mode can be set using [`set_nonblocking`].
451452
///
453+
/// Passing a listener in blocking mode is always erroneous,
454+
/// and the behavior in that case may change in the future.
455+
/// For example, it could panic.
456+
///
452457
/// [`set_nonblocking`]: std::os::unix::net::UnixDatagram::set_nonblocking
453458
///
454459
/// # Panics
@@ -484,6 +489,8 @@ impl UnixDatagram {
484489
/// ```
485490
#[track_caller]
486491
pub fn from_std(datagram: net::UnixDatagram) -> io::Result<UnixDatagram> {
492+
check_socket_for_blocking(&datagram)?;
493+
487494
let socket = mio::net::UnixDatagram::from_std(datagram);
488495
let io = PollEvented::new(socket)?;
489496
Ok(UnixDatagram { io })

tokio/src/net/unix/listener.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::io::{Interest, PollEvented};
22
use crate::net::unix::{SocketAddr, UnixStream};
3+
use crate::util::check_socket_for_blocking;
34

45
use std::fmt;
56
use std::io;
@@ -106,6 +107,10 @@ impl UnixListener {
106107
/// will block the thread, which will cause unexpected behavior.
107108
/// Non-blocking mode can be set using [`set_nonblocking`].
108109
///
110+
/// Passing a listener in blocking mode is always erroneous,
111+
/// and the behavior in that case may change in the future.
112+
/// For example, it could panic.
113+
///
109114
/// [`set_nonblocking`]: std::os::unix::net::UnixListener::set_nonblocking
110115
///
111116
/// # Examples
@@ -133,6 +138,8 @@ impl UnixListener {
133138
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
134139
#[track_caller]
135140
pub fn from_std(listener: net::UnixListener) -> io::Result<UnixListener> {
141+
check_socket_for_blocking(&listener)?;
142+
136143
let listener = mio::net::UnixListener::from_std(listener);
137144
let io = PollEvented::new(listener)?;
138145
Ok(UnixListener { io })

tokio/src/net/unix/stream.rs

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::net::unix::split::{split, ReadHalf, WriteHalf};
33
use crate::net::unix::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf};
44
use crate::net::unix::ucred::{self, UCred};
55
use crate::net::unix::SocketAddr;
6+
use crate::util::check_socket_for_blocking;
67

78
use std::fmt;
89
use std::future::poll_fn;
@@ -791,6 +792,10 @@ impl UnixStream {
791792
/// will block the thread, which will cause unexpected behavior.
792793
/// Non-blocking mode can be set using [`set_nonblocking`].
793794
///
795+
/// Passing a listener in blocking mode is always erroneous,
796+
/// and the behavior in that case may change in the future.
797+
/// For example, it could panic.
798+
///
794799
/// [`set_nonblocking`]: std::os::unix::net::UnixStream::set_nonblocking
795800
///
796801
/// # Examples
@@ -818,6 +823,8 @@ impl UnixStream {
818823
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
819824
#[track_caller]
820825
pub fn from_std(stream: net::UnixStream) -> io::Result<UnixStream> {
826+
check_socket_for_blocking(&stream)?;
827+
821828
let stream = mio::net::UnixStream::from_std(stream);
822829
let io = PollEvented::new(stream)?;
823830

tokio/src/util/blocking_check.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#[cfg(unix)]
2+
use std::os::fd::AsFd;
3+
4+
#[cfg(unix)]
5+
#[allow(unused_variables)]
6+
#[track_caller]
7+
pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
8+
#[cfg(not(tokio_allow_from_blocking_fd))]
9+
{
10+
let sock = socket2::SockRef::from(s);
11+
12+
debug_assert!(
13+
sock.nonblocking()?,
14+
"Registering a blocking socket with the tokio runtime is unsupported. \
15+
If you wish to do anyways, please add `--cfg tokio_allow_from_blocking_fd` to your \
16+
RUSTFLAGS. See github.com/tokio-rs/tokio/issues/7172 for details."
17+
);
18+
}
19+
20+
Ok(())
21+
}
22+
23+
#[cfg(not(unix))]
24+
#[allow(unused_variables)]
25+
pub(crate) fn check_socket_for_blocking<S>(s: &S) -> crate::io::Result<()> {
26+
// we cannot retrieve the nonblocking status on windows
27+
// and i dont know how to support wasi yet
28+
Ok(())
29+
}

tokio/src/util/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ cfg_io_driver! {
55
#[cfg(feature = "rt")]
66
pub(crate) mod atomic_cell;
77

8+
#[cfg(feature = "net")]
9+
mod blocking_check;
10+
#[cfg(feature = "net")]
11+
#[allow(unused_imports)]
12+
pub(crate) use blocking_check::check_socket_for_blocking;
13+
814
pub(crate) mod metric_atomics;
915

1016
#[cfg(any(feature = "rt", feature = "signal", feature = "process"))]

tokio/tests/io_driver.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,10 @@ fn panics_when_io_disabled() {
9696
let rt = runtime::Builder::new_current_thread().build().unwrap();
9797

9898
rt.block_on(async {
99-
let _ =
100-
tokio::net::TcpListener::from_std(std::net::TcpListener::bind("127.0.0.1:0").unwrap());
99+
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
100+
101+
listener.set_nonblocking(true).unwrap();
102+
103+
let _ = tokio::net::TcpListener::from_std(listener);
101104
});
102105
}

tokio/tests/io_driver_drop.rs

+6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ fn tcp_doesnt_block() {
1313
let listener = {
1414
let _enter = rt.enter();
1515
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
16+
17+
listener.set_nonblocking(true).unwrap();
18+
1619
TcpListener::from_std(listener).unwrap()
1720
};
1821

@@ -33,6 +36,9 @@ fn drop_wakes() {
3336
let listener = {
3437
let _enter = rt.enter();
3538
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
39+
40+
listener.set_nonblocking(true).unwrap();
41+
3642
TcpListener::from_std(listener).unwrap()
3743
};
3844

tokio/tests/no_rt.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,9 @@ async fn timeout_value() {
3939
)]
4040
#[cfg_attr(miri, ignore)] // No `socket` in miri.
4141
fn io_panics_when_no_tokio_context() {
42-
let _ = tokio::net::TcpListener::from_std(std::net::TcpListener::bind("127.0.0.1:0").unwrap());
42+
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
43+
44+
listener.set_nonblocking(true).unwrap();
45+
46+
let _ = tokio::net::TcpListener::from_std(listener);
4347
}

tokio/tests/tcp_peek.rs

+7
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,18 @@ use std::{io::Write, net};
1313
#[tokio::test]
1414
async fn peek() {
1515
let listener = net::TcpListener::bind("127.0.0.1:0").unwrap();
16+
1617
let addr = listener.local_addr().unwrap();
1718
let t = thread::spawn(move || assert_ok!(listener.accept()).0);
1819

1920
let left = net::TcpStream::connect(addr).unwrap();
21+
22+
left.set_nonblocking(true).unwrap();
23+
2024
let mut right = t.join().unwrap();
25+
26+
right.set_nonblocking(true).unwrap();
27+
2128
let _ = right.write(&[1, 2, 3, 4]).unwrap();
2229

2330
let mut left: TcpStream = left.try_into().unwrap();

0 commit comments

Comments
 (0)