Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rxrpc: Fix notification vs call-release vs recvmsg

When a call is released, rxrpc takes the spinlock and removes it from
->recvmsg_q in an effort to prevent racing recvmsg() invocations from
seeing the same call. Now, rxrpc_recvmsg() only takes the spinlock when
actually removing a call from the queue; it doesn't, however, take it in
the lead up to that when it checks to see if the queue is empty. It *does*
hold the socket lock, which prevents a recvmsg/recvmsg race - but this
doesn't prevent sendmsg from ending the call because sendmsg() drops the
socket lock and relies on the call->user_mutex.

Fix this by firstly removing the bit in rxrpc_release_call() that dequeues
the released call and, instead, rely on recvmsg() to simply discard
released calls (done in a preceding fix).

Secondly, rxrpc_notify_socket() is abandoned if the call is already marked
as released rather than trying to be clever by setting both pointers in
call->recvmsg_link to NULL to trick list_empty(). This isn't perfect and
can still race, resulting in a released call on the queue, but recvmsg()
will now clean that up.

Fixes: 17926a79320a ("[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both")
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Jeffrey Altman <jaltman@auristor.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Junvyyang, Tencent Zhuque Lab <zhuque@tencent.com>
cc: LePremierHomme <kwqcheii@proton.me>
cc: Simon Horman <horms@kernel.org>
cc: linux-afs@lists.infradead.org
Link: https://patch.msgid.link/20250717074350.3767366-4-dhowells@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

David Howells and committed by
Jakub Kicinski
2fd89584 962fb1f6

+18 -17
+2 -1
include/trace/events/rxrpc.h
··· 322 322 EM(rxrpc_call_put_kernel, "PUT kernel ") \ 323 323 EM(rxrpc_call_put_poke, "PUT poke ") \ 324 324 EM(rxrpc_call_put_recvmsg, "PUT recvmsg ") \ 325 + EM(rxrpc_call_put_release_recvmsg_q, "PUT rls-rcmq") \ 325 326 EM(rxrpc_call_put_release_sock, "PUT rls-sock") \ 326 327 EM(rxrpc_call_put_release_sock_tba, "PUT rls-sk-a") \ 327 328 EM(rxrpc_call_put_sendmsg, "PUT sendmsg ") \ 328 - EM(rxrpc_call_put_unnotify, "PUT unnotify") \ 329 329 EM(rxrpc_call_put_userid_exists, "PUT u-exists") \ 330 330 EM(rxrpc_call_put_userid, "PUT user-id ") \ 331 331 EM(rxrpc_call_see_accept, "SEE accept ") \ ··· 338 338 EM(rxrpc_call_see_disconnected, "SEE disconn ") \ 339 339 EM(rxrpc_call_see_distribute_error, "SEE dist-err") \ 340 340 EM(rxrpc_call_see_input, "SEE input ") \ 341 + EM(rxrpc_call_see_notify_released, "SEE nfy-rlsd") \ 341 342 EM(rxrpc_call_see_recvmsg, "SEE recvmsg ") \ 342 343 EM(rxrpc_call_see_release, "SEE release ") \ 343 344 EM(rxrpc_call_see_userid_exists, "SEE u-exists") \
+12 -16
net/rxrpc/call_object.c
··· 561 561 void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call) 562 562 { 563 563 struct rxrpc_connection *conn = call->conn; 564 - bool put = false, putu = false; 564 + bool putu = false; 565 565 566 566 _enter("{%d,%d}", call->debug_id, refcount_read(&call->ref)); 567 567 ··· 573 573 574 574 rxrpc_put_call_slot(call); 575 575 576 - /* Make sure we don't get any more notifications */ 576 + /* Note that at this point, the call may still be on or may have been 577 + * added back on to the socket receive queue. recvmsg() must discard 578 + * released calls. The CALL_RELEASED flag should prevent further 579 + * notifications. 580 + */ 577 581 spin_lock_irq(&rx->recvmsg_lock); 578 - 579 - if (!list_empty(&call->recvmsg_link)) { 580 - _debug("unlinking once-pending call %p { e=%lx f=%lx }", 581 - call, call->events, call->flags); 582 - list_del(&call->recvmsg_link); 583 - put = true; 584 - } 585 - 586 - /* list_empty() must return false in rxrpc_notify_socket() */ 587 - call->recvmsg_link.next = NULL; 588 - call->recvmsg_link.prev = NULL; 589 - 590 582 spin_unlock_irq(&rx->recvmsg_lock); 591 - if (put) 592 - rxrpc_put_call(call, rxrpc_call_put_unnotify); 593 583 594 584 write_lock(&rx->call_lock); 595 585 ··· 626 636 rxrpc_abort_call_sock_release); 627 637 rxrpc_release_call(rx, call); 628 638 rxrpc_put_call(call, rxrpc_call_put_release_sock); 639 + } 640 + 641 + while ((call = list_first_entry_or_null(&rx->recvmsg_q, 642 + struct rxrpc_call, recvmsg_link))) { 643 + list_del_init(&call->recvmsg_link); 644 + rxrpc_put_call(call, rxrpc_call_put_release_recvmsg_q); 629 645 } 630 646 631 647 _leave("");
+4
net/rxrpc/recvmsg.c
··· 29 29 30 30 if (!list_empty(&call->recvmsg_link)) 31 31 return; 32 + if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) { 33 + rxrpc_see_call(call, rxrpc_call_see_notify_released); 34 + return; 35 + } 32 36 33 37 rcu_read_lock(); 34 38