From: David Howells <dhowells@redhat.com>
To: netdev@vger.kernel.org
Cc: David Howells <dhowells@redhat.com>,
Marc Dionne <marc.dionne@auristor.com>,
Jakub Kicinski <kuba@kernel.org>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Paolo Abeni <pabeni@redhat.com>, Simon Horman <horms@kernel.org>,
linux-afs@lists.infradead.org, linux-kernel@vger.kernel.org,
stable@kernel.org
Subject: [PATCH net v8 03/12] rxrpc: Fix packet encryption error handling
Date: Mon, 24 Aug 2026 10:16:35 +0100 [thread overview]
Message-ID: <20260824091645.415423-4-dhowells@redhat.com> (raw)
In-Reply-To: <20260824091645.415423-1-dhowells@redhat.com>
In rxrpc_send_data(), if ->secure_packet() returns an error, the code
currently just jumps to out: and returns the error to the app on the
assumption that any error returned by this is automatically fatal for the
call, and may even have corrupted the transmission queue - but leaving it
to userspace to deal with. Nothing stops the application from retrying the
sendmsg(), which will try to encrypt the buffer again, and might succeed
with a corrupt buffer.
Fix rxrpc_send_data() in the following ways:
(1) If -ENOMEM is returned, assume we never got as far as the encryption
and that the operation is retryable. In which case, jump to
maybe_error_rewind and, if we've copied data into the last packet,
remove some of the bytes from it that we just added so that we don't
tell the caller that we've completed the transmission phase. The
iterator is also correspondingly rewound.
(2) If any other error occurs, set the TX_ERROR flag on the call and
return that error directly; on all subsequent attempts to add data to
the call, return -EIO. The app must then abort the call to get rid of
it (this allows the app to choose the abort code to use).
afs_make_call() and afs_send_simple_reply() are also modified to repeat
calls to rxrpc_kernel_send_data() if less than a full transfer was made.
Fixes: 17926a79320a ("[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both")
Closes: https://sashiko.dev/#/patchset/20260702144919.172295-1-dhowells%40redhat.com
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Eric Dumazet <edumazet@google.com>
cc: "David S. Miller" <davem@davemloft.net>
cc: Jakub Kicinski <kuba@kernel.org>
cc: Paolo Abeni <pabeni@redhat.com>
cc: Simon Horman <horms@kernel.org>
cc: linux-afs@lists.infradead.org
cc: stable@kernel.org
---
Documentation/networking/rxrpc.rst | 11 ++++--
fs/afs/rxrpc.c | 38 ++++++++++++--------
include/trace/events/rxrpc.h | 1 +
net/rxrpc/ar-internal.h | 1 +
net/rxrpc/sendmsg.c | 58 ++++++++++++++++++++++++------
5 files changed, 82 insertions(+), 27 deletions(-)
diff --git a/Documentation/networking/rxrpc.rst b/Documentation/networking/rxrpc.rst
index 8926dab8e2e6..7df6aff7644c 100644
--- a/Documentation/networking/rxrpc.rst
+++ b/Documentation/networking/rxrpc.rst
@@ -879,14 +879,21 @@ The kernel interface functions are as follows:
exclusively to in-kernel virtual addresses. msg.msg_flags may be given
MSG_MORE if there will be subsequent data sends for this call.
- The msg must not specify a destination address, control data or any flags
- other than MSG_MORE. len is the total amount of data to transmit.
+ msg must not specify a destination address, control data or any flags
+ other than MSG_MORE. len is the amount of data to add to the
+ transmission. The last-packet flag will only be set on the outgoing
+ packet if MSG_MORE is not set and len amount of bytes are buffered.
notify_end_rx can be NULL or it can be used to specify a function to be
called when the call changes state to end the Tx phase. This function is
called with a spinlock held to prevent the last DATA packet from being
transmitted until the function returns.
+ The function returns the amount of data buffered or an error. It will
+ return zero only if len is 0 or if msg->msg_iter is empty. It may also
+ make a short write, buffering less than the amount of data provided or the
+ len specified, in which case it should be called again.
+
(#) Receive data from a call::
int rxrpc_kernel_recv_data(struct socket *sock,
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index e35b49a904eb..bf0d231d30a6 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -412,26 +412,32 @@ void afs_make_call(struct afs_call *call, gfp_t gfp)
msg.msg_controllen = 0;
msg.msg_flags = MSG_WAITALL | (call->write_iter ? MSG_MORE : 0);
- ret = rxrpc_kernel_send_data(call->net->socket, rxcall,
- &msg, call->request_size,
- afs_notify_end_request_tx);
- if (ret < 0)
- goto error_do_abort;
+ do {
+ ret = rxrpc_kernel_send_data(call->net->socket, rxcall, &msg,
+ msg_data_left(&msg),
+ afs_notify_end_request_tx);
+ if (ret < 0)
+ goto error_do_abort;
+ } while (msg_data_left(&msg) > 0);
if (call->write_iter) {
msg.msg_iter = *call->write_iter;
msg.msg_flags &= ~MSG_MORE;
trace_afs_send_data(call, &msg);
- ret = rxrpc_kernel_send_data(call->net->socket,
- call->rxcall, &msg,
- iov_iter_count(&msg.msg_iter),
- afs_notify_end_request_tx);
+ do {
+ ret = rxrpc_kernel_send_data(call->net->socket,
+ call->rxcall, &msg,
+ msg_data_left(&msg),
+ afs_notify_end_request_tx);
+ if (ret < 0) {
+ trace_afs_sent_data(call, &msg, ret);
+ goto error_do_abort;
+ }
+ } while (msg_data_left(&msg) > 0);
*call->write_iter = msg.msg_iter;
- trace_afs_sent_data(call, &msg, ret);
- if (ret < 0)
- goto error_do_abort;
+ trace_afs_sent_data(call, &msg, 0);
}
/* Note that at this point, we may have received the reply or an abort
@@ -912,8 +918,12 @@ void afs_send_simple_reply(struct afs_call *call, const void *buf, size_t len)
msg.msg_controllen = 0;
msg.msg_flags = 0;
- n = rxrpc_kernel_send_data(net->socket, call->rxcall, &msg, len,
- afs_notify_end_reply_tx);
+ do {
+ n = rxrpc_kernel_send_data(net->socket, call->rxcall,
+ &msg, msg_data_left(&msg),
+ afs_notify_end_reply_tx);
+ } while (n >= 0 && msg_data_left(&msg) > 0);
+
if (n >= 0) {
/* Success */
_leave(" [replied]");
diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
index 704a10de6670..8f3e3967885a 100644
--- a/include/trace/events/rxrpc.h
+++ b/include/trace/events/rxrpc.h
@@ -148,6 +148,7 @@
EM(rxrpc_eproto_wrong_security, "wrong-sec") \
EM(rxrpc_recvmsg_excess_data, "recvmsg-excess") \
EM(rxrpc_recvmsg_short_data, "recvmsg-short") \
+ EM(rxrpc_sendmsg_tx_error, "tx-error") \
E_(rxrpc_sendmsg_late_send, "sendmsg-late")
#define rxrpc_call_poke_traces \
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 865f05fe37ab..a6f830c1621f 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -642,6 +642,7 @@ enum rxrpc_call_flag {
RXRPC_CALL_TX_LAST, /* Last packet in Tx buffer (at rxtx_top) */
RXRPC_CALL_TX_ALL_ACKED, /* Last packet has been hard-acked */
RXRPC_CALL_TX_NO_MORE, /* No more data to transmit (MSG_MORE deasserted) */
+ RXRPC_CALL_TX_ERROR, /* Terminal error; call needs abort */
RXRPC_CALL_SEND_PING, /* A ping will need to be sent */
RXRPC_CALL_RETRANS_TIMEOUT, /* Retransmission due to timeout occurred */
RXRPC_CALL_BEGAN_RX_TIMER, /* We began the expect_rx_by timer */
diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c
index 565799548102..3a36f82b84d9 100644
--- a/net/rxrpc/sendmsg.c
+++ b/net/rxrpc/sendmsg.c
@@ -330,13 +330,6 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
bool more = msg->msg_flags & MSG_MORE;
int ret, copied = 0;
- if (test_bit(RXRPC_CALL_TX_NO_MORE, &call->flags)) {
- trace_rxrpc_abort(call->debug_id, rxrpc_sendmsg_late_send,
- call->cid, call->call_id, call->rx_consumed,
- 0, -EPROTO);
- return -EPROTO;
- }
-
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
ret = rxrpc_wait_to_be_connected(call, &timeo);
@@ -353,6 +346,19 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
reload:
+ if (unlikely(test_bit(RXRPC_CALL_TX_NO_MORE, &call->flags))) {
+ trace_rxrpc_abort(call->debug_id, rxrpc_sendmsg_late_send,
+ call->cid, call->call_id, call->rx_consumed,
+ 0, -EPROTO);
+ return -EPROTO;
+ }
+ if (unlikely(test_bit(RXRPC_CALL_TX_ERROR, &call->flags))) {
+ trace_rxrpc_abort(call->debug_id, rxrpc_sendmsg_tx_error,
+ call->cid, call->call_id, call->rx_consumed,
+ 0, -EIO);
+ return -EIO;
+ }
+
txb = call->tx_pending;
call->tx_pending = NULL;
if (txb)
@@ -441,12 +447,26 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
/* add the packet to the send queue if it's now full */
if (!txb->space ||
(len == 0 && !more)) {
- if (len == 0 && !more)
- txb->flags |= RXRPC_LAST_PACKET;
-
+ /* Do any required crypto. If this fails, it could
+ * have corrupted the txbuf content with a partial
+ * encrypt. Assume that ENOMEM is retryable, but
+ * everything else is terminal.
+ */
ret = call->security->secure_packet(call, txb);
- if (ret < 0)
+ if (ret < 0) {
+ /* Assume that ENOMEM here means that the
+ * encryption hasn't happened yet. The data is
+ * aligned to avoid the need for slow buffering
+ * in the crypto walk.
+ */
+ if (ret == -ENOMEM)
+ goto maybe_error_rewind;
+ set_bit(RXRPC_CALL_TX_ERROR, &call->flags);
goto out;
+ }
+
+ if (len == 0 && !more)
+ txb->flags |= RXRPC_LAST_PACKET;
rxrpc_queue_packet(rx, call, txb, notify_end_tx);
txb = NULL;
}
@@ -464,6 +484,22 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
_leave(" = %d", call->error);
return call->error;
+maybe_error_rewind:
+ /* If we got a retryable error after copying all the supplied data into
+ * the last packet, we need to rewind as much as we can so the caller
+ * knows they need to retry the sendmsg.
+ */
+ if (copied && !more && !len) {
+ unsigned int rewind_by = umin(copied, txb->len);
+
+ txb->space += rewind_by;
+ txb->len -= rewind_by;
+ txb->offset -= rewind_by;
+ copied -= rewind_by;
+ if (call->tx_total_len != -1)
+ call->tx_total_len += rewind_by;
+ iov_iter_revert(&msg->msg_iter, rewind_by);
+ }
maybe_error:
if (copied) {
if (rxrpc_call_is_complete(call) &&
next prev parent reply other threads:[~2026-08-24 9:17 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-24 9:16 [PATCH net v8 00/12] rxrpc: Fix CHALLENGE packet handling David Howells
2026-08-24 9:16 ` [PATCH net v8 01/12] rxrpc: Fix sendmsg to not return an error if last packet queued David Howells
2026-08-24 9:16 ` [PATCH net v8 02/12] rxrpc: Fix sendmsg length David Howells
2026-08-24 9:16 ` David Howells [this message]
2026-08-24 9:16 ` [PATCH net v8 04/12] rxrpc: Fix update of call->tx_pending without holding lock David Howells
2026-08-24 9:16 ` [PATCH net v8 05/12] rxrpc: Fix double IRQ enablement David Howells
2026-08-24 9:16 ` [PATCH net v8 06/12] rxrpc: Fix generation of notifications after call completion David Howells
2026-08-24 9:16 ` [PATCH net v8 07/12] rxrpc: Expand abort trace enum David Howells
2026-08-24 9:16 ` [PATCH net v8 08/12] keys: Add refcounting to user-defined key type payload David Howells
2026-08-24 9:16 ` [PATCH net v8 09/12] afs: Create a server appdata key David Howells
2026-08-24 9:16 ` [PATCH net v8 10/12] rxrpc: Pass appdata key to rxrpc_call and thence to rxrpc_bundle David Howells
2026-08-24 9:16 ` [PATCH net v8 11/12] rxrpc: Fix CHALLENGE packet overqueuing and simplify RESPONSE generation David Howells
2026-08-24 9:16 ` [PATCH net v8 12/12] rxrpc: Remove OOB challenge/response code David Howells
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260824091645.415423-4-dhowells@redhat.com \
--to=dhowells@redhat.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-afs@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=marc.dionne@auristor.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stable@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®