[PATCH net 3/4] afs: Fix UAF when sending a message
From: David Howells <dhowells@redhat.com>
Date: 2026-07-02 14:49:49
Also in:
lkml
Subsystem:
afs filesystem, filesystems (vfs and infrastructure), the rest, tracing · Maintainers:
David Howells, Marc Dionne, Alexander Viro, Christian Brauner, Linus Torvalds, Steven Rostedt, Masami Hiramatsu
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@auristor.com>
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Jeffrey Altman <redacted>
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
---
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);