There's a potential UAF in afs_make_call() in the event that an asynchronous call is being sent, but the call fails in some way (e.g. it gets aborted from the server). The problem is that afs_make_call() tries to abort a call if the rxrpc send fails, but the asynchronous notification from rxrpc may have caused the afs_call to be torn down. Fix this making afs_make_op_call() give the op->call its own ref rather than transferring the caller's ref to it and then dropping the ref when afs_make_call() returns. This also means that the afs_make_call() func never loses its ref on the call now. Fixes: e49c7b2f6de7 ("afs: Build an abstraction around an "operation" concept") Link: https://sashiko.dev/#/patchset/20260702144919.172295-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Eric Dumazet cc: "David S. Miller" cc: Jakub Kicinski cc: Paolo Abeni cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org --- fs/afs/internal.h | 3 ++- fs/afs/rxrpc.c | 3 --- include/trace/events/afs.h | 2 ++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 7b68cf141bf9..47e2cae979b6 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -1418,7 +1418,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c { struct afs_addr_list *alist = op->estate->addresses; - op->call = call; + op->call = afs_get_call(call, afs_call_trace_get_op_call); op->type = call->type; call->op = op; call->key = op->key; @@ -1426,6 +1426,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c call->peer = rxrpc_kernel_get_peer(alist->addrs[op->addr_index].peer); call->service_id = op->server->service_id; afs_make_call(call, gfp); + afs_put_call(call, afs_call_trace_put_made_call); } static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size) diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index a1b9ced4e0f4..6dc6fd853832 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -382,8 +382,6 @@ void afs_make_call(struct afs_call *call, gfp_t gfp) if (ret < 0) goto error_do_abort; - /* We lost our ref on call if MSG_MORE was not set and ret >= 0. */ - if (write_iter) { msg.msg_iter = *call->write_iter; msg.msg_flags &= ~MSG_MORE; @@ -393,7 +391,6 @@ 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); - /* We lost our ref on call if ret >= 0. */ trace_afs_sent_data(debug_id, &msg, ret); if (ret < 0) diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index df397c11df85..a1963e21f034 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h @@ -122,8 +122,10 @@ enum yfs_cm_operation { #define afs_call_traces \ EM(afs_call_trace_alloc, "ALLOC ") \ EM(afs_call_trace_free, "FREE ") \ + EM(afs_call_trace_get_op_call, "GET op ") \ EM(afs_call_trace_get_make_async_call, "GET a-make ") \ EM(afs_call_trace_put_async_complete, "PUT a-cmpl ") \ + EM(afs_call_trace_put_made_call, "PUT made ") \ EM(afs_call_trace_put_discard_prealloc, "PUT dis-pre") \ EM(afs_call_trace_put_get_capabilities, "PUT get-cap") \ EM(afs_call_trace_put_giveupcallbacks, "PUT gvup-cb") \