ktls: Fix races that can lead to double initialization
ktls_enable_rx() and ktls_enable_tx() have checks to return EALREADY if the socket already has KTLS enabled. However, these are done without any locks held and nothing blocks concurrent attempts to set the socket option. I believe the worst outcome of the race is leaked memory. Fix the problem by rechecking under the sockbuf lock. While here, unify the locking protocol for sb_tls_info: require both the sockbuf and socket I/O locks in order to enable KTLS. This means that either lock is sufficient for checking whether KTLS is enabled in a given sockbuf, which simplifies some refactoring further down the road. Note that the SOLISTENING() check can go away because SOCK_IO_RECV_LOCK() atomically locks the socket buffer and checks whether the socket is a listening socket. This changes the returned errno value, so update a test which checks it. Reviewed by: gallatin MFC after: 2 weeks Sponsored by: Klara, Inc. Sponsored by: Stormshield Differential Revision: https://reviews.freebsd.org/D45674
This commit is contained in:
+21
-2
@@ -1320,12 +1320,23 @@ ktls_enable_rx(struct socket *so, struct tls_enable *en)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Serialize with soreceive_generic() and make sure that we're not
|
||||
* operating on a listening socket.
|
||||
*/
|
||||
error = SOCK_IO_RECV_LOCK(so, SBL_WAIT);
|
||||
if (error) {
|
||||
ktls_free(tls);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Mark the socket as using TLS offload. */
|
||||
SOCK_RECVBUF_LOCK(so);
|
||||
if (SOLISTENING(so)) {
|
||||
if (__predict_false(so->so_rcv.sb_tls_info != NULL)) {
|
||||
SOCK_RECVBUF_UNLOCK(so);
|
||||
SOCK_IO_RECV_UNLOCK(so);
|
||||
ktls_free(tls);
|
||||
return (EINVAL);
|
||||
return (EALREADY);
|
||||
}
|
||||
so->so_rcv.sb_tls_seqno = be64dec(en->rec_seq);
|
||||
so->so_rcv.sb_tls_info = tls;
|
||||
@@ -1335,6 +1346,7 @@ ktls_enable_rx(struct socket *so, struct tls_enable *en)
|
||||
sb_mark_notready(&so->so_rcv);
|
||||
ktls_check_rx(&so->so_rcv);
|
||||
SOCK_RECVBUF_UNLOCK(so);
|
||||
SOCK_IO_RECV_UNLOCK(so);
|
||||
|
||||
/* Prefer TOE -> ifnet TLS -> software TLS. */
|
||||
#ifdef TCP_OFFLOAD
|
||||
@@ -1420,6 +1432,13 @@ ktls_enable_tx(struct socket *so, struct tls_enable *en)
|
||||
inp = so->so_pcb;
|
||||
INP_WLOCK(inp);
|
||||
SOCK_SENDBUF_LOCK(so);
|
||||
if (__predict_false(so->so_snd.sb_tls_info != NULL)) {
|
||||
SOCK_SENDBUF_UNLOCK(so);
|
||||
INP_WUNLOCK(inp);
|
||||
SOCK_IO_SEND_UNLOCK(so);
|
||||
ktls_free(tls);
|
||||
return (EALREADY);
|
||||
}
|
||||
so->so_snd.sb_tls_seqno = be64dec(en->rec_seq);
|
||||
so->so_snd.sb_tls_info = tls;
|
||||
if (tls->mode != TCP_TLS_MODE_SW) {
|
||||
|
||||
+2
-1
@@ -128,7 +128,8 @@ struct sockbuf {
|
||||
struct mbuf *sb_mtls; /* TLS mbuf chain */
|
||||
struct mbuf *sb_mtlstail; /* last mbuf in TLS chain */
|
||||
uint64_t sb_tls_seqno; /* TLS seqno */
|
||||
struct ktls_session *sb_tls_info; /* TLS state */
|
||||
/* TLS state, locked by sockbuf and sock I/O mutexes. */
|
||||
struct ktls_session *sb_tls_info;
|
||||
};
|
||||
/*
|
||||
* PF_UNIX/SOCK_DGRAM
|
||||
|
||||
@@ -2812,7 +2812,7 @@ ATF_TC_BODY(ktls_listening_socket, tc)
|
||||
TLS_MINOR_VER_THREE, (uint64_t)random(), &en);
|
||||
ATF_REQUIRE_ERRNO(ENOTCONN,
|
||||
setsockopt(s, IPPROTO_TCP, TCP_TXTLS_ENABLE, &en, sizeof(en)) != 0);
|
||||
ATF_REQUIRE_ERRNO(EINVAL,
|
||||
ATF_REQUIRE_ERRNO(ENOTCONN,
|
||||
setsockopt(s, IPPROTO_TCP, TCP_RXTLS_ENABLE, &en, sizeof(en)) != 0);
|
||||
ATF_REQUIRE(close(s) == 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user