nfsd: fix possible fh_compose of wrong dentry in nfsd4_create_file()

dentry_create() can hypothetically provide a different dentry than the
one passed in.  This could happen, for example, if the exported
filesystem is NFS, and the server returned to OPEN a filehandle which
matched a directory that was already in the dcache.  Clearly this would
not be expected!

If this were to happen the dentry (child) that was already stored in
resfhp could be freed and later dereferenced.

We shouldn't call fh_compose() until we are certain that we have the
final dentry, so this patch moved the fh_compose() call to two places:
one for the case where the target already exists, and one after
dentry_create() where it was created.

Fixes: 64a989dbd1 ("VFS/knfsd: Teach dentry_create() to use atomic_open()")
Cc: stable@vger.kernel.org
Signed-off-by: NeilBrown <neil@brown.name>
Link: https://patch.msgid.link/20260526053004.4014491-2-neilb@ownmail.net
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Benjamin Coddington <bcodding@hammerspace.com>
This commit is contained in:
NeilBrown
2026-05-26 15:27:58 +10:00
committed by Chuck Lever
parent 036c1b182f
commit 3e2c79360c

View File

@@ -306,10 +306,6 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
goto out;
}
status = fh_compose(resfhp, fhp->fh_export, child, fhp);
if (status != nfs_ok)
goto out;
v_mtime = 0;
v_atime = 0;
if (nfsd4_create_is_exclusive(open->op_createmode)) {
@@ -335,6 +331,10 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (status != nfs_ok)
goto out;
status = fh_compose(resfhp, fhp->fh_export, child, fhp);
if (status != nfs_ok)
goto out;
switch (open->op_createmode) {
case NFS4_CREATE_UNCHECKED:
if (!d_is_reg(child))
@@ -385,6 +385,10 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
open->op_created = true;
fh_fill_post_attrs(fhp);
status = fh_compose(resfhp, fhp->fh_export, child, fhp);
if (status != nfs_ok)
goto out;
/* A newly created file already has a file size of zero. */
if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0))
iap->ia_valid &= ~ATTR_SIZE;