[PATCH 1/5] NFSv4/flexfiles: bound the multipath address count

Junrui Luo via B4 Relay <[email protected]> Tue, 04 Aug 2026 18:55:01 +0800
Newsgroups gmane.linux.kernel,gmane.linux.nfs,gmane.linux.kernel.stable
Message-ID <[email protected]>
From: Junrui Luo <[email protected]>

nfs4_ff_alloc_deviceid_node() decodes the multipath address list of a
flexfiles device from the GETDEVICEINFO reply body. mp_count is taken
straight off the wire and used as the loop bound for the
nfs4_decode_mp_ds_addr() calls. nfs4_get_device_info() bounds pdev->pglen
but not the counts encoded inside it, so a server can advertise up to
2^32 - 1 addresses in a body that holds none of them.

Once the xdr_stream is exhausted the loop stops making progress:
xdr_stream_decode_string_dup() fails in xdr_stream_decode_opaque_inline()
, so nfs4_decode_mp_ds_addr() returns NULL without consuming a byte and
every remaining iteration re-fails at the same offset. With mp_count set
to 0xffffffff this spins for roughly 2^32 iterations with no rescheduling
point, potentially tripping the soft lockup watchdog and the RCU stall
detector on non-preemptible kernels. The list_empty(&dsaddrs) check
below the loop only runs once the loop has finished.

Reject implausible counts up front, as ff_layout_alloc_lseg() already
does for mirror_array_cnt and dss_count.

Fixes: d67ae825a59d ("pnfs/flexfiles: Add the FlexFile Layout Driver")
Reported-by: Yuhao Jiang <[email protected]>
Assisted-by: Claude:claude-opus-5
Cc: [email protected]
Signed-off-by: Junrui Luo <[email protected]>
---
 fs/nfs/flexfilelayout/flexfilelayout.h    | 4 ++++
 fs/nfs/flexfilelayout/flexfilelayoutdev.c | 5 +++++
 2 files changed, 9 insertions(+)

diff --git a/fs/nfs/flexfilelayout/flexfilelayout.h b/fs/nfs/flexfilelayout/flexfilelayout.h
index a5bd00f69e82..d024b8db4ce0 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.h
+++ b/fs/nfs/flexfilelayout/flexfilelayout.h
@@ -23,6 +23,10 @@
 
 #define NFS4_FLEXFILE_LAYOUT_MAX_STRIPE_CNT 4096
 
+/* Filter out insanely large multipath address counts, which would
+ * otherwise let a server spin the GETDEVICEINFO decode loop. */
+#define NFS4_FLEXFILE_LAYOUT_MAX_MULTIPATH_CNT 4096
+
 /* LAYOUTSTATS report interval in ms */
 #define FF_LAYOUTSTATS_REPORT_INTERVAL (60000L)
 #define FF_LAYOUTSTATS_MAXDEV 4
diff --git a/fs/nfs/flexfilelayout/flexfilelayoutdev.c b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
index 1109462a9699..659a2bf7b502 100644
--- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c
+++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
@@ -79,6 +79,11 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
 	mp_count = be32_to_cpup(p);
 	dprintk("%s: multipath ds count %d\n", __func__, mp_count);
 
+	if (mp_count > NFS4_FLEXFILE_LAYOUT_MAX_MULTIPATH_CNT) {
+		ret = -EINVAL;
+		goto out_err_drain_dsaddrs;
+	}
+
 	for (i = 0; i < mp_count; i++) {
 		/* multipath ds */
 		da = nfs4_decode_mp_ds_addr(net, &stream, gfp_flags);

-- 
2.51.2