]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
nfsd: fix refcount leak in nfsd_set_fh_dentry()
authorNeilBrown <neil@brown.name>
Wed, 8 Oct 2025 13:52:25 +0000 (09:52 -0400)
committerChuck Lever <chuck.lever@oracle.com>
Tue, 4 Nov 2025 16:02:31 +0000 (11:02 -0500)
nfsd exports a "pseudo root filesystem" which is used by NFSv4 to find
the various exported filesystems using LOOKUP requests from a known root
filehandle.  NFSv3 uses the MOUNT protocol to find those exported
filesystems and so is not given access to the pseudo root filesystem.

If a v3 (or v2) client uses a filehandle from that filesystem,
nfsd_set_fh_dentry() will report an error, but still stores the export
in "struct svc_fh" even though it also drops the reference (exp_put()).
This means that when fh_put() is called an extra reference will be dropped
which can lead to use-after-free and possible denial of service.

Normal NFS usage will not provide a pseudo-root filehandle to a v3
client.  This bug can only be triggered by the client synthesising an
incorrect filehandle.

To fix this we move the assignments to the svc_fh later, after all
possible error cases have been detected.

Reported-and-tested-by: tianshuo han <hantianshuo233@gmail.com>
Fixes: ef7f6c4904d0 ("nfsd: move V4ROOT version check to nfsd_set_fh_dentry()")
Signed-off-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Cc: stable@vger.kernel.org
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfsd/nfsfh.c

index 3edccc38db42e56d5652f65c487c90c42f209d68..bd9acfdc7b0149c2e38bbdd45d4db472cc273ce7 100644 (file)
@@ -269,9 +269,6 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct net *net,
                                dentry);
        }
 
-       fhp->fh_dentry = dentry;
-       fhp->fh_export = exp;
-
        switch (fhp->fh_maxsize) {
        case NFS4_FHSIZE:
                if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOATOMIC_ATTR)
@@ -293,6 +290,9 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct net *net,
                        goto out;
        }
 
+       fhp->fh_dentry = dentry;
+       fhp->fh_export = exp;
+
        return 0;
 out:
        exp_put(exp);