aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2021-05-21 21:44:46 +0000
committerMark Johnston <markj@FreeBSD.org>2021-06-04 01:02:02 +0000
commit75683ed20b70da2b813e5a9da220cfee94c0b307 (patch)
treee4cfdc1b936ac9258e6cb118cca3ff6d8d2a8166
parent0f05c6f3a5274d07f17e7e2ca5a7d286ae37c09b (diff)
downloadsrc-75683ed20b70da2b813e5a9da220cfee94c0b307.tar.gz
src-75683ed20b70da2b813e5a9da220cfee94c0b307.zip
Fix handling of errors from pru_send(PRUS_NOTREADY)
PRUS_NOTREADY indicates that the caller has not yet populated the chain with data, and so it is not ready for transmission. This is used by sendfile (for async I/O) and KTLS (for encryption). In particular, if pru_send returns an error, the caller is responsible for freeing the chain since other implicit references to the data buffers exist. For async sendfile, it happens that an error will only be returned if the connection was dropped, in which case tcp_usr_ready() will handle freeing the chain. But since KTLS can be used in conjunction with the regular socket I/O system calls, many more error cases - which do not result in the connection being dropped - are reachable. In these cases, KTLS was effectively assuming success. So: - Change sosend_generic() to free the mbuf chain if pru_send(PRUS_NOTREADY) fails. Nothing else owns a reference to the chain at that point. - Similarly, in vn_sendfile() change the !async I/O && KTLS case to free the chain. - If async I/O is still outstanding when pru_send fails in vn_sendfile(), set an error in the sfio structure so that the connection is aborted and the mbuf chain is freed. Reviewed by: gallatin, tuexen Discussed with: jhb Sponsored by: The FreeBSD Foundation (cherry picked from commit 916c61a5ed37da8ecdedd3c5512813d8dcec9a24)
-rw-r--r--sys/kern/kern_sendfile.c12
-rw-r--r--sys/kern/uipc_socket.c19
2 files changed, 15 insertions, 16 deletions
diff --git a/sys/kern/kern_sendfile.c b/sys/kern/kern_sendfile.c
index 520b7c9c62d0..ac1072ca2406 100644
--- a/sys/kern/kern_sendfile.c
+++ b/sys/kern/kern_sendfile.c
@@ -1175,8 +1175,12 @@ prepend_header:
if (tls != NULL && tls->mode == TCP_TLS_MODE_SW) {
error = (*so->so_proto->pr_usrreqs->pru_send)
(so, PRUS_NOTREADY, m, NULL, NULL, td);
- soref(so);
- ktls_enqueue(m, so, tls_enq_cnt);
+ if (error != 0) {
+ m_freem(m);
+ } else {
+ soref(so);
+ ktls_enqueue(m, so, tls_enq_cnt);
+ }
} else
#endif
error = (*so->so_proto->pr_usrreqs->pru_send)
@@ -1187,11 +1191,11 @@ prepend_header:
soref(so);
error = (*so->so_proto->pr_usrreqs->pru_send)
(so, PRUS_NOTREADY, m, NULL, NULL, td);
- sendfile_iodone(sfio, NULL, 0, 0);
+ sendfile_iodone(sfio, NULL, 0, error);
}
CURVNET_RESTORE();
- m = NULL; /* pru_send always consumes */
+ m = NULL;
if (error)
goto done;
sbytes += space + hdrlen;
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index 77ce876fd20f..852132e45e58 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -1763,18 +1763,13 @@ restart:
#ifdef KERN_TLS
if (tls != NULL && tls->mode == TCP_TLS_MODE_SW) {
- /*
- * Note that error is intentionally
- * ignored.
- *
- * Like sendfile(), we rely on the
- * completion routine (pru_ready())
- * to free the mbufs in the event that
- * pru_send() encountered an error and
- * did not append them to the sockbuf.
- */
- soref(so);
- ktls_enqueue(top, so, tls_enq_cnt);
+ if (error != 0) {
+ m_freem(top);
+ top = NULL;
+ } else {
+ soref(so);
+ ktls_enqueue(top, so, tls_enq_cnt);
+ }
}
#endif
clen = 0;