[PATCH] nfsd: block non-SAVEFH ops after FOREIGN PUTFH to prevent NULL deref

Jeff Layton posted 1 patch 1 week, 5 days ago
fs/nfsd/nfs4proc.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
[PATCH] nfsd: block non-SAVEFH ops after FOREIGN PUTFH to prevent NULL deref
Posted by Jeff Layton 1 week, 5 days ago
When CONFIG_NFSD_V4_2_INTER_SSC is enabled, nfsd4_putfh() can return
success with fh_dentry and fh_export both NULL if fh_verify() returns
nfserr_stale and putfh->no_verify is true. The NFSD4_FH_FOREIGN flag
is set, but the compound dispatch loop only uses this flag to bypass
the nfserr_nofilehandle check -- it does not prevent subsequent ops
from running with a NULL fh_dentry.

A remote client can exploit this by crafting a COMPOUND that includes
an inter-SSC COPY (which causes check_if_stalefh_allowed() to set
no_verify=true on the saved PUTFH) with an additional op inserted
between the source PUTFH and SAVEFH. For example, SETATTR calls
fh_want_write() which dereferences fh_export->ex_path.mnt without
calling fh_verify() first, causing a NULL pointer dereference in the
nfsd kthread.

Fix this by gating the dispatch loop: when NFSD4_FH_FOREIGN is set
and fh_dentry is NULL, only OP_SAVEFH (needed for the inter-SSC flow)
and ops with ALLOWED_WITHOUT_FH (which don't need a resolved
filehandle) may proceed. All other ops receive nfserr_stale, per
RFC 7862 Section 15.2.3 which specifies that foreign filehandle
validation is deferred to the consuming operation and NFS4ERR_STALE
returned at that point.

Fixes: 3ad685d73ed8 ("NFSD: allow inter server COPY to have a STALE source server fh")
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/nfsd/nfs4proc.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 34f2921e4ef8..00cd8dd460fc 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -3138,9 +3138,22 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
 				op->status = nfsd4_open_omfg(rqstp, cstate, op);
 			goto encode_op;
 		}
-		if (!current_fh->fh_dentry &&
-				!HAS_FH_FLAG(current_fh, NFSD4_FH_FOREIGN)) {
-			if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
+		if (!current_fh->fh_dentry) {
+			if (HAS_FH_FLAG(current_fh, NFSD4_FH_FOREIGN)) {
+				/*
+				 * FOREIGN fh from inter-SSC PUTFH: only
+				 * SAVEFH may proceed with a NULL fh_dentry.
+				 * Per RFC 7862 S15.2.3, validation of a
+				 * foreign fh is deferred to the operation
+				 * that consumes it, and NFS4ERR_STALE is
+				 * returned at that point.
+				 */
+				if (op->opnum != OP_SAVEFH &&
+				    !(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
+					op->status = nfserr_stale;
+					goto encode_op;
+				}
+			} else if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
 				op->status = nfserr_nofilehandle;
 				goto encode_op;
 			}

---
base-commit: b69fc3eaa867d0caa904634ea7a1b4569411b163
change-id: 20260527-putfh_foreign_fh_null_deref_consumers-083ac712fc5b

Best regards,
-- 
Jeff Layton <jlayton@kernel.org>
Re: [PATCH] nfsd: block non-SAVEFH ops after FOREIGN PUTFH to prevent NULL deref
Posted by Chuck Lever 1 week, 4 days ago
From: Chuck Lever <chuck.lever@oracle.com>

On Wed, 27 May 2026 10:53:37 -0400, Jeff Layton wrote:
> When CONFIG_NFSD_V4_2_INTER_SSC is enabled, nfsd4_putfh() can return
> success with fh_dentry and fh_export both NULL if fh_verify() returns
> nfserr_stale and putfh->no_verify is true. The NFSD4_FH_FOREIGN flag
> is set, but the compound dispatch loop only uses this flag to bypass
> the nfserr_nofilehandle check -- it does not prevent subsequent ops
> from running with a NULL fh_dentry.
> 
> [...]

Applied to nfsd-testing, thanks!

[1/1] nfsd: block non-SAVEFH ops after FOREIGN PUTFH to prevent NULL deref
      commit: d8db9ced22a876091c432c4adb35e5d05be196aa

--
Chuck Lever <chuck.lever@oracle.com>