| From c97d79af26547edffbba0ae17d2b1195e37a4ffd Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 9 Dec 2025 14:30:15 -0500 |
| Subject: net/handshake: duplicate handshake cancellations leak socket |
| |
| From: Scott Mayhew <smayhew@redhat.com> |
| |
| [ Upstream commit 15564bd67e2975002f2a8e9defee33e321d3183f ] |
| |
| When a handshake request is cancelled it is removed from the |
| handshake_net->hn_requests list, but it is still present in the |
| handshake_rhashtbl until it is destroyed. |
| |
| If a second cancellation request arrives for the same handshake request, |
| then remove_pending() will return false... and assuming |
| HANDSHAKE_F_REQ_COMPLETED isn't set in req->hr_flags, we'll continue |
| processing through the out_true label, where we put another reference on |
| the sock and a refcount underflow occurs. |
| |
| This can happen for example if a handshake times out - particularly if |
| the SUNRPC client sends the AUTH_TLS probe to the server but doesn't |
| follow it up with the ClientHello due to a problem with tlshd. When the |
| timeout is hit on the server, the server will send a FIN, which triggers |
| a cancellation request via xs_reset_transport(). When the timeout is |
| hit on the client, another cancellation request happens via |
| xs_tls_handshake_sync(). |
| |
| Add a test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED) in the pending cancel |
| path so duplicate cancels can be detected. |
| |
| Fixes: 3b3009ea8abb ("net/handshake: Create a NETLINK service for handling handshake requests") |
| Suggested-by: Chuck Lever <chuck.lever@oracle.com> |
| Signed-off-by: Scott Mayhew <smayhew@redhat.com> |
| Reviewed-by: Chuck Lever <chuck.lever@oracle.com> |
| Link: https://patch.msgid.link/20251209193015.3032058-1-smayhew@redhat.com |
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/handshake/request.c | 6 +++++- |
| 1 file changed, 5 insertions(+), 1 deletion(-) |
| |
| diff --git a/net/handshake/request.c b/net/handshake/request.c |
| index 94d5cef3e048b..0ac126b0add60 100644 |
| --- a/net/handshake/request.c |
| +++ b/net/handshake/request.c |
| @@ -325,7 +325,11 @@ bool handshake_req_cancel(struct sock *sk) |
| |
| hn = handshake_pernet(net); |
| if (hn && remove_pending(hn, req)) { |
| - /* Request hadn't been accepted */ |
| + /* Request hadn't been accepted - mark cancelled */ |
| + if (test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED, &req->hr_flags)) { |
| + trace_handshake_cancel_busy(net, req, sk); |
| + return false; |
| + } |
| goto out_true; |
| } |
| if (test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED, &req->hr_flags)) { |
| -- |
| 2.51.0 |
| |