Skip to content

Commit 2fd8958

Browse files
dhowellskuba-moo
authored andcommitted
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: 17926a7 ("[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both") Signed-off-by: David Howells <[email protected]> Reviewed-by: Jeffrey Altman <[email protected]> cc: Marc Dionne <[email protected]> cc: Junvyyang, Tencent Zhuque Lab <[email protected]> cc: LePremierHomme <[email protected]> cc: Simon Horman <[email protected]> cc: [email protected] Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 962fb1f commit 2fd8958

File tree

3 files changed

+18
-17
lines changed

3 files changed

+18
-17
lines changed

include/trace/events/rxrpc.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,10 @@
322322
EM(rxrpc_call_put_kernel, "PUT kernel ") \
323323
EM(rxrpc_call_put_poke, "PUT poke ") \
324324
EM(rxrpc_call_put_recvmsg, "PUT recvmsg ") \
325+
EM(rxrpc_call_put_release_recvmsg_q, "PUT rls-rcmq") \
325326
EM(rxrpc_call_put_release_sock, "PUT rls-sock") \
326327
EM(rxrpc_call_put_release_sock_tba, "PUT rls-sk-a") \
327328
EM(rxrpc_call_put_sendmsg, "PUT sendmsg ") \
328-
EM(rxrpc_call_put_unnotify, "PUT unnotify") \
329329
EM(rxrpc_call_put_userid_exists, "PUT u-exists") \
330330
EM(rxrpc_call_put_userid, "PUT user-id ") \
331331
EM(rxrpc_call_see_accept, "SEE accept ") \
@@ -338,6 +338,7 @@
338338
EM(rxrpc_call_see_disconnected, "SEE disconn ") \
339339
EM(rxrpc_call_see_distribute_error, "SEE dist-err") \
340340
EM(rxrpc_call_see_input, "SEE input ") \
341+
EM(rxrpc_call_see_notify_released, "SEE nfy-rlsd") \
341342
EM(rxrpc_call_see_recvmsg, "SEE recvmsg ") \
342343
EM(rxrpc_call_see_release, "SEE release ") \
343344
EM(rxrpc_call_see_userid_exists, "SEE u-exists") \

net/rxrpc/call_object.c

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ static void rxrpc_cleanup_rx_buffers(struct rxrpc_call *call)
561561
void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call)
562562
{
563563
struct rxrpc_connection *conn = call->conn;
564-
bool put = false, putu = false;
564+
bool putu = false;
565565

566566
_enter("{%d,%d}", call->debug_id, refcount_read(&call->ref));
567567

@@ -573,23 +573,13 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call)
573573

574574
rxrpc_put_call_slot(call);
575575

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+
*/
577581
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-
590582
spin_unlock_irq(&rx->recvmsg_lock);
591-
if (put)
592-
rxrpc_put_call(call, rxrpc_call_put_unnotify);
593583

594584
write_lock(&rx->call_lock);
595585

@@ -638,6 +628,12 @@ void rxrpc_release_calls_on_socket(struct rxrpc_sock *rx)
638628
rxrpc_put_call(call, rxrpc_call_put_release_sock);
639629
}
640630

631+
while ((call = list_first_entry_or_null(&rx->recvmsg_q,
632+
struct rxrpc_call, recvmsg_link))) {
633+
list_del_init(&call->recvmsg_link);
634+
rxrpc_put_call(call, rxrpc_call_put_release_recvmsg_q);
635+
}
636+
641637
_leave("");
642638
}
643639

net/rxrpc/recvmsg.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ void rxrpc_notify_socket(struct rxrpc_call *call)
2929

3030
if (!list_empty(&call->recvmsg_link))
3131
return;
32+
if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) {
33+
rxrpc_see_call(call, rxrpc_call_see_notify_released);
34+
return;
35+
}
3236

3337
rcu_read_lock();
3438

0 commit comments

Comments
 (0)