[PATCH] NFSv4.1: fix out-of-bounds read decoding GETDEVICEINFO notification bitmap

Chuyf26 <Chuyf26-KPsoFbNs7GizrGE5bRqYAgC/[email protected]>
Newsgroups gmane.linux.nfs
Message-ID <[email protected]>
decode_getdeviceinfo() passes 4 * len to a single xdr_inline_decode()
call, where len is the word count of the notification bitmap supplied
by the server.  The multiplication can wrap around to a small value,
so the bounds check only covers a few bytes while the following loop
reads len words from the resulting pointer.  A malicious NFS server
can trigger a large out-of-bounds read in the client by replying to
GETDEVICEINFO with a huge bitmap word count.

The path is: a pNFS client issues GETDEVICEINFO while discovering a
layout, and the reply is decoded by nfs4_xdr_dec_getdeviceinfo() ->
decode_getdeviceinfo().  With a wrapped 4 * len the pointer returned
by xdr_inline_decode() sits at the very end of the receive buffer, and
the loop then reads up to nearly a gigabyte of words past it.  The XDR
receive buffer is built from pages, not slab objects, so KASAN does
not instrument these reads; depending on what follows in memory the
loop either consumes garbage notification bits or walks into unmapped
memory.

Decode the bitmap one word at a time so every read is bounds checked.

Fixes: 4e59080397fa ("NFSv4.1: Allow getdeviceinfo to return notification info back to caller")
Reported-by: Abaci <abaci-KPsoFbNs7GizrGE5bRqYAgC/[email protected]>
Assisted-by: abaci:qwen3.8-max
Signed-off-by: Chuyf26 <Chuyf26-KPsoFbNs7GizrGE5bRqYAgC/[email protected]>
---
 fs/nfs/nfs4xdr.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index c8e073ef1d37..582f571689dc 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -5958,13 +5958,13 @@ static int decode_getdeviceinfo(struct xdr_stream *xdr,
 	if (len) {
 		uint32_t i;
 
-		p = xdr_inline_decode(xdr, 4 * len);
-		if (unlikely(!p))
-			return -EIO;
-
-		res->notification = be32_to_cpup(p++);
-		for (i = 1; i < len; i++) {
-			if (be32_to_cpup(p++)) {
+		for (i = 0; i < len; i++) {
+			p = xdr_inline_decode(xdr, 4);
+			if (unlikely(!p))
+				return -EIO;
+			if (i == 0)
+				res->notification = be32_to_cpup(p);
+			else if (be32_to_cpup(p)) {
 				dprintk("%s: unsupported notification\n",
 					__func__);
 				return -EIO;
-- 
2.43.5
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.