[PATCH] NFSv4: fix out-of-bounds read when decoding many pNFS layout types
Chuyf26 <[email protected]>
| Newsgroups | org.kernel.vger.linux-nfs |
|---|---|
| Message-ID | <20260818162332.Z32qq4QUMQSjH5g-YvgOUsu882RBUL3Qgv3pfhxxEUg@z> |
decode_pnfs_layout_types() passes nlayouttypes * 4 to a single
xdr_inline_decode() call. nlayouttypes comes from the server and the
multiplication can wrap around to a small value, so the bounds check
only covers a few bytes while the following loop reads up to
NFS_MAX_LAYOUT_TYPES words from the resulting pointer. A malicious
NFS server can trigger an out-of-bounds read in the client at mount
time by announcing a huge number of layout types.
The path is: the client issues FSINFO (for instance during mount or
statfs), and the reply is decoded by nfs4_xdr_dec_fsinfo() ->
decode_fsinfo() -> decode_pnfs_layout_types(). With a wrapped
nlayouttypes * 4 the pointer returned by xdr_inline_decode() sits at
the very end of the receive buffer, and the loop then reads up to
NFS_MAX_LAYOUT_TYPES words past it. The XDR receive buffer is built
from pages, not slab objects, so KASAN does not instrument these reads
and the following memory is normally still mapped: the access silently
produces garbage layout type values instead of failing the decode.
Cap the number of layout types before reading them and decode each
layout type word individually so every read is bounds checked.
Fixes: 3132e49ecef9 ("pnfs: track multiple layout types in fsinfo structure")
Reported-by: Abaci <[email protected]>
Assisted-by: abaci:qwen3.8-max
Signed-off-by: Chuyf26 <[email protected]>
---
fs/nfs/nfs4xdr.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index a9d57fcdf9b4..c8e073ef1d37 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -4816,11 +4816,6 @@ static int decode_pnfs_layout_types(struct xdr_stream *xdr,
if (fsinfo->nlayouttypes == 0)
return 0;
- /* Decode and set first layout type, move xdr->p past unused types */
- p = xdr_inline_decode(xdr, fsinfo->nlayouttypes * 4);
- if (unlikely(!p))
- return -EIO;
-
/* If we get too many, then just cap it at the max */
if (fsinfo->nlayouttypes > NFS_MAX_LAYOUT_TYPES) {
printk(KERN_INFO "NFS: %s: Warning: Too many (%u) pNFS layout types\n",
@@ -4828,8 +4823,16 @@ static int decode_pnfs_layout_types(struct xdr_stream *xdr,
fsinfo->nlayouttypes = NFS_MAX_LAYOUT_TYPES;
}
- for(i = 0; i < fsinfo->nlayouttypes; ++i)
- fsinfo->layouttype[i] = be32_to_cpup(p++);
+ /* Decode and set the first layout types, moving xdr->p past all
+ * announced words one at a time: a single xdr_inline_decode() of
+ * nlayouttypes * 4 bytes can wrap around for huge values.
+ */
+ for (i = 0; i < fsinfo->nlayouttypes; ++i) {
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(!p))
+ return -EIO;
+ fsinfo->layouttype[i] = be32_to_cpup(p);
+ }
return 0;
}
--
2.43.5