Skip to content

Commit d843eb1

Browse files
authored
Remove unsafety from setting TcpStream keepalive (#1187)
We now have a way to convert `tokio` -> `std` `TcpStream`s and use `socket2`'s `Socket::From<TcpStream>` impl to safely set a socket's keepalive option. This eliminates the need for us to use `unsafe` in the keepalive setter. Signed-off-by: Kevin Leimkuhler <[email protected]>
1 parent 2706350 commit d843eb1

File tree

3 files changed

+14
-40
lines changed

3 files changed

+14
-40
lines changed

linkerd/proxy/transport/src/connect.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl<T: Param<Remote<ServerAddr>>> tower::Service<T> for ConnectTcp {
3737
Box::pin(async move {
3838
let io = TcpStream::connect(&addr).await?;
3939
super::set_nodelay_or_warn(&io);
40-
super::set_keepalive_or_warn(&io, keepalive);
40+
let io = super::set_keepalive_or_warn(io, keepalive)?;
4141
debug!(
4242
local.addr = %io.local_addr().expect("cannot load local addr"),
4343
?keepalive,

linkerd/proxy/transport/src/lib.rs

Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! Utilities for use TCP servers & clients.
22
//!
3-
//! Uses unsafe code to interact with socket options for keepalive and SO_ORIGINAL_DST.
3+
//! Uses unsafe code to interact with socket options for SO_ORIGINAL_DST.
44
55
#![deny(warnings, rust_2018_idioms)]
6-
//#![forbid(unsafe_code)]
6+
// #![forbid(unsafe_code)]
77

88
pub mod addrs;
99
mod connect;
@@ -17,6 +17,7 @@ pub use self::{
1717
listen::{Bind, BindTcp},
1818
orig_dst::BindWithOrigDst,
1919
};
20+
use linkerd_io as io;
2021
use socket2::TcpKeepalive;
2122
use std::time::Duration;
2223
use tokio::net::TcpStream;
@@ -38,47 +39,20 @@ fn set_nodelay_or_warn(socket: &TcpStream) {
3839
}
3940
}
4041

41-
fn set_keepalive_or_warn(tcp: &TcpStream, keepalive_duration: Option<Duration>) {
42-
// TODO(eliza): when https://github.com/tokio-rs/tokio/pull/3189 merges
43-
// upstream, we will be able to convert the Tokio `TcpStream` into a
44-
// `socket2::Socket` without unsafe, by converting it to a
45-
// `std::net::TcpStream` (as `socket2::Socket` has a
46-
// `From<std::net::TcpStream>`). What we're doing now is more or less
47-
// equivalent, but this would use a safe interface...
48-
#[cfg(unix)]
49-
let sock = unsafe {
50-
// Safety: `from_raw_fd` takes ownership of the underlying file
51-
// descriptor, and will close it when dropped. However, we obtain the
52-
// file descriptor via `as_raw_fd` rather than `into_raw_fd`, so the
53-
// Tokio `TcpStream` *also* retains ownership of the socket --- which is
54-
// what we want. Instead of letting the `socket2` socket returned by
55-
// `from_raw_fd` close the fd, we `mem::forget` the `Socket`, so that
56-
// its `Drop` impl will not run. This ensures the fd is not closed
57-
// prematurely.
58-
use std::os::unix::io::{AsRawFd, FromRawFd};
59-
socket2::Socket::from_raw_fd(tcp.as_raw_fd())
42+
fn set_keepalive_or_warn(
43+
tcp: TcpStream,
44+
keepalive_duration: Option<Duration>,
45+
) -> io::Result<TcpStream> {
46+
let sock = {
47+
let stream = tokio::net::TcpStream::into_std(tcp)?;
48+
socket2::Socket::from(stream)
6049
};
61-
#[cfg(windows)]
62-
let sock = unsafe {
63-
// Safety: `from_raw_socket` takes ownership of the underlying Windows
64-
// SOCKET, and will close it when dropped. However, we obtain the
65-
// SOCKET via `as_raw_socket` rather than `into_raw_socket`, so the
66-
// Tokio `TcpStream` *also* retains ownership of the socket --- which is
67-
// what we want. Instead of letting the `socket2` socket returned by
68-
// `from_raw_socket` close the SOCKET, we `mem::forget` the `Socket`, so
69-
// that its `Drop` impl will not run. This ensures the socket is not
70-
// closed prematurely.
71-
use std::os::windows::io::{AsRawSocket, FromRawSocket};
72-
socket2::Socket::from_raw_socket(tcp.as_raw_socket())
73-
};
74-
7550
let ka = keepalive_duration
7651
.into_iter()
7752
.fold(TcpKeepalive::new(), |k, t| k.with_time(t));
7853
if let Err(e) = sock.set_tcp_keepalive(&ka) {
7954
tracing::warn!("failed to set keepalive: {}", e);
8055
}
81-
82-
// Don't let the socket2 socket close the fd on drop!
83-
std::mem::forget(sock);
56+
let stream: std::net::TcpStream = socket2::Socket::into(sock);
57+
tokio::net::TcpStream::from_std(stream)
8458
}

linkerd/proxy/transport/src/listen.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ where
6666
let accept = TcpListenerStream::new(listen).map(move |res| {
6767
let tcp = res?;
6868
super::set_nodelay_or_warn(&tcp);
69-
super::set_keepalive_or_warn(&tcp, keepalive);
69+
let tcp = super::set_keepalive_or_warn(tcp, keepalive)?;
7070
let client = Remote(ClientAddr(tcp.peer_addr()?));
7171
Ok((Addrs { server, client }, tcp))
7272
});

0 commit comments

Comments
 (0)