lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <tencent_DBA407008574A1E4BF7F27AD38AD4D13730A@qq.com>
Date: Mon, 26 Jan 2026 14:25:24 +0800
From: "jack.zhou" <jack.wemmick@...mail.com>
To: Trond Myklebust <trondmy@...nel.org>
Cc: linux-nfs@...r.kernel.org,
	netdev@...r.kernel.org,
	"jack.zhou" <jacketzc@...look.com>
Subject: [PATCH] sunrpc/xs_read_stream: fix dead loop when xs_read_discard

From: "jack.zhou" <jacketzc@...look.com>

Hi maintainers,

This bug was discovered during following scenario:

1. Using NFS client with TCP mode
2. RPC_REPLY process


Let's first review the normal process:

1. The `xs_read_stream` function is located within the for loop of the `xs_stream_data_receive` function. The condition for exiting is that the return value ret of the `xs_read_stream` method is < 0.
2. When entering the function `xs_read_stream`, since `transport->recv.len` == 0, `xs_read_stream_header` will be called first. Based on `transport->recv.calldir`, it decides whether to execute `RPC_CALL` or `RPC_REPLY`;(and also sets the value of `transport->recv.len`)
3. Since it is executing `RPC_REPLY`, the return value of the function `xs_read_stream_reply` is provided by tcp.c's `tcp_recvmsg`, indicating the size of the copied skb, and this value is > 0.
4. Since it is a normal process, it will reach the end of the function and return, which is: 
```
transport->recv.offset = 0;
transport->recv.len = 0;
return read;
```
5. The returned 'read' is the return value of the function 'xs_read_stream_reply' in the RPC_REPLY process. Since this value is > 0, the `xs_stream_data_receive` function's for loop cannot be exited.
6. After re-entering the xs_read_stream function, since transport->recv.len == 0, xs_read_stream_header will still be called.
7. Since the skb is empty, tcp.c's `tcp_recvmsg` will return a value < 0, usually -EAGAIN (-11).
8. Since the return value of xs_read_stream_header is < 0, the goto out_err statement will be executed.
9. The for loop of the xs_stream_data_receive function will be exited.
10. The next time xs_stream_data_receive is entered, the above steps will be repeated.


Now we are encountering an abnormal process:


1. For some reason, skb is contaminated, and `transport->recv.calldir` parses out an incorrect value, so `msg.msg_flags |= MSG_TRUNC`;(and also sets the value of transport->recv.len)
2. The `if (transport->recv.offset < transport->recv.len) {}` condition will be executed
3. The return value of function `xs_read_discard` is > 0, so when returning to the upstream `xs_stream_data_receive` function, the for loop cannot be exited
4. When entering xs_read_stream again, since transport->recv.len != 0, `xs_read_stream_header` will no longer be executed, which means `transport->recv.calldir` cannot be correctly set anymore
5. This is fatal for all subsequent RPC Replies because `transport->recv.calldir` cannot be correctly set anymore, so they will all go to `xs_read_discard`
6. At the same time, since transport->recv.len has not been reset to 0, this loop will become an infinite loop

Therefore, my approach is:

After reaching the point of `xs_read_discard` due to an abnormal situation, the value of `transport->recv.len` will be reset in the same way as a normal return, in order to prevent a deadlock from occurring.

The modification has been tested and passed in version 5.10.160.



---
 net/sunrpc/xprtsock.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 2e1fe6013361..91d1b992eb7f 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -695,6 +695,13 @@ xs_read_stream_reply(struct sock_xprt *transport, struct msghdr *msg, int flags)
 	return ret;
 }
 
+static void
+xs_stream_reset_recv(struct sock_xprt *transport)
+{
+	transport->recv.offset = 0;
+	transport->recv.len = 0;
+}
+
 static ssize_t
 xs_read_stream(struct sock_xprt *transport, int flags)
 {
@@ -740,8 +747,10 @@ xs_read_stream(struct sock_xprt *transport, int flags)
 		msg.msg_flags = 0;
 		ret = xs_read_discard(transport->sock, &msg, flags,
 				transport->recv.len - transport->recv.offset);
-		if (ret <= 0)
+		if (ret <= 0) {
+			xs_stream_reset_recv(transport);
 			goto out_err;
+		}
 		transport->recv.offset += ret;
 		read += ret;
 		if (transport->recv.offset != transport->recv.len)
@@ -751,8 +760,7 @@ xs_read_stream(struct sock_xprt *transport, int flags)
 		trace_xs_stream_read_request(transport);
 		transport->recv.copied = 0;
 	}
-	transport->recv.offset = 0;
-	transport->recv.len = 0;
+	xs_stream_reset_recv(transport);
 	return read;
 out_err:
 	return ret != 0 ? ret : -ESHUTDOWN;
-- 
2.51.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ