[PATCH net 3/4] afs: Fix UAF when sending a message

Marc Dionne marc.c.dionne at gmail.com
Fri Jul 3 04:12:05 PDT 2026


On Thu, Jul 2, 2026 at 11:49 AM David Howells <dhowells at redhat.com> wrote:
>
> In afs_make_call(), there's a race with async call reception and
> destruction.  If a call is dispatched that doesn't have call->write_iter
> set (used to specify the data content for FS.StoreData), then the first
> rxrpc_kernel_send_data() will not set MSG_MORE in the msghdr.
>
> Once rxrpc_send_data() queues the last request packet, the response could
> come in at any time and cause the call to be completed and put.  However,
> afs_make_call() will look at the call again to see it ->write_iter should
> be handled - something it's only allowed to do if it has its own ref on the
> call.  Whilst this is the case for synchronous calls, it isn't true for
> async calls such as FS.FetchData.
>
> generic/650 plays games with randomly taking CPUs offline, and can
> interject a significant delay such that the call is deallocated before
> afs_make_call() gets to check call->write_iter - and a UAF ensues (caught
> by KASAN).
>
>    BUG: KASAN: slab-use-after-free in afs_make_call+0x1c90/0x2210 [kafs]
>    Read of size 8 at addr ffff888035e050e8 by task fsstress/1409
>
> Fix this by caching the call->write_iter and call->debug_id so that neither
> variable needs to be accessed after the first send.
>
> Fixes: eddf51f2bb2c ("afs: Make {Y,}FS.FetchData an asynchronous operation")
> Reported-by: Marc Dionne <marc.dionne at auristor.com>
> Signed-off-by: David Howells <dhowells at redhat.com>
> cc: Jeffrey Altman <jaltman at auristor.com>
> cc: Eric Dumazet <edumazet at google.com>
> cc: "David S. Miller" <davem at davemloft.net>
> cc: Jakub Kicinski <kuba at kernel.org>
> cc: Paolo Abeni <pabeni at redhat.com>
> cc: Simon Horman <horms at kernel.org>
> cc: linux-afs at lists.infradead.org
> cc: stable at kernel.org
> ---
>  fs/afs/rxrpc.c             | 12 ++++++++----
>  include/trace/events/afs.h |  6 +++---
>  2 files changed, 11 insertions(+), 7 deletions(-)
>
> diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
> index d82916657a3d..05fcb9b6adde 100644
> --- a/fs/afs/rxrpc.c
> +++ b/fs/afs/rxrpc.c
> @@ -347,7 +347,9 @@ void afs_make_call(struct afs_call *call, gfp_t gfp)
>         struct rxrpc_call *rxcall;
>         struct msghdr msg;
>         struct kvec iov[1];
> +       unsigned int debug_id = call->debug_id;
>         size_t len;
> +       bool write_iter = call->write_iter;
>         s64 tx_total_len;
>         int ret;
>
> @@ -410,7 +412,7 @@ void afs_make_call(struct afs_call *call, gfp_t gfp)
>         iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, iov, 1, call->request_size);
>         msg.msg_control         = NULL;
>         msg.msg_controllen      = 0;
> -       msg.msg_flags           = MSG_WAITALL | (call->write_iter ? MSG_MORE : 0);
> +       msg.msg_flags           = MSG_WAITALL | (write_iter ? MSG_MORE : 0);
>
>         ret = rxrpc_kernel_send_data(call->net->socket, rxcall,
>                                      &msg, call->request_size,
> @@ -418,7 +420,9 @@ void afs_make_call(struct afs_call *call, gfp_t gfp)
>         if (ret < 0)
>                 goto error_do_abort;
>
> -       if (call->write_iter) {
> +       /* We lost our ref on call if MSG_MORE was set. */
> +
> +       if (write_iter) {
>                 msg.msg_iter = *call->write_iter;
>                 msg.msg_flags &= ~MSG_MORE;
>                 trace_afs_send_data(call, &msg);
> @@ -427,9 +431,9 @@ void afs_make_call(struct afs_call *call, gfp_t gfp)
>                                              call->rxcall, &msg,
>                                              iov_iter_count(&msg.msg_iter),
>                                              afs_notify_end_request_tx);
> -               *call->write_iter = msg.msg_iter;
> +               /* We lost our ref on call. */
>
> -               trace_afs_sent_data(call, &msg, ret);
> +               trace_afs_sent_data(debug_id, &msg, ret);
>                 if (ret < 0)
>                         goto error_do_abort;
>         }
> diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h
> index 1b3c48b5591d..cf7218efb861 100644
> --- a/include/trace/events/afs.h
> +++ b/include/trace/events/afs.h
> @@ -937,9 +937,9 @@ TRACE_EVENT(afs_send_data,
>             );
>
>  TRACE_EVENT(afs_sent_data,
> -           TP_PROTO(struct afs_call *call, struct msghdr *msg, int ret),
> +           TP_PROTO(unsigned int call_debug_id, struct msghdr *msg, int ret),
>
> -           TP_ARGS(call, msg, ret),
> +           TP_ARGS(call_debug_id, msg, ret),
>
>             TP_STRUCT__entry(
>                     __field(unsigned int,               call)
> @@ -949,7 +949,7 @@ TRACE_EVENT(afs_sent_data,
>                              ),
>
>             TP_fast_assign(
> -                   __entry->call = call->debug_id;
> +                   __entry->call = call_debug_id;
>                     __entry->ret = ret;
>                     __entry->offset = msg->msg_iter.xarray_start + msg->msg_iter.iov_offset;
>                     __entry->count = iov_iter_count(&msg->msg_iter);
>
>

Reviewed-by: Marc Dionne <marc.dionne at auristor.com>

Marc



More information about the linux-afs mailing list