summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Layton <jlayton@kernel.org>2026-05-21 19:37:33 +0300
committerChuck Lever <cel@kernel.org>2026-06-09 23:32:59 +0300
commit24c975bbdd564d7d0ad90294bfa69729830345de (patch)
treee2c733d7e862e412f63972f95acf6daf3c79bfd1
parente186fa1c057f5eccb22afb1e83e34c0627085868 (diff)
downloadlinux-24c975bbdd564d7d0ad90294bfa69729830345de.tar.xz
nfsd: fix posix_acl leak and ignored error in nfsd4_create_file
nfsd4_create_file() has two bugs in its ACL handling: The return value of nfsd4_acl_to_attr() is silently discarded. When the NFSv4-to-POSIX ACL conversion fails (e.g., -EINVAL for unsupported ACE types), the file is created without any ACL and the client receives NFS4_OK. This violates RFC 7530/8881 which require the server to reject unsupported attributes on CREATE. When start_creating() fails after ACL attributes have been populated in attrs (either via nfsd4_acl_to_attr or via ownership transfer from open->op_dpacl/op_pacl), the function jumps to out_write which skips nfsd_attrs_free(). The posix_acl allocations are leaked. A client can trigger this repeatedly with OPEN(CREATE), ACL attributes, and an invalid filename (e.g., longer than NAME_MAX). Fix both by capturing the nfsd4_acl_to_attr() return value and by changing the early error paths to jump to out instead of out_write. Initialize child to ERR_PTR(-EINVAL) so that end_creating() is safe to call even if start_creating() was never reached. Reported-by: Chris Mason <clm@meta.com> Fixes: 7ab96df840e6 ("VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating()") Cc: stable@vger.kernel.org Assisted-by: kres:claude-opus-4-6 Signed-off-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
-rw-r--r--fs/nfsd/nfs4proc.c15
1 files changed, 9 insertions, 6 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 14e329cfdfa6..8561540ab2db 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -253,7 +253,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
.na_iattr = iap,
.na_seclabel = &open->op_label,
};
- struct dentry *parent, *child;
+ struct dentry *parent, *child = ERR_PTR(-EINVAL);
__u32 v_mtime, v_atime;
struct inode *inode;
__be32 status;
@@ -277,10 +277,14 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (open->op_acl) {
if (open->op_dpacl || open->op_pacl) {
status = nfserr_inval;
- goto out_write;
+ goto out;
+ }
+ if (is_create_with_attrs(open)) {
+ status = nfsd4_acl_to_attr(NF4REG, open->op_acl,
+ &attrs);
+ if (status)
+ goto out;
}
- if (is_create_with_attrs(open))
- nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs);
} else if (is_create_with_attrs(open)) {
/* The dpacl and pacl will get released by nfsd_attrs_free(). */
attrs.na_dpacl = open->op_dpacl;
@@ -293,7 +297,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
&QSTR_LEN(open->op_fname, open->op_fnamelen));
if (IS_ERR(child)) {
status = nfserrno(PTR_ERR(child));
- goto out_write;
+ goto out;
}
if (d_really_is_negative(child)) {
@@ -407,7 +411,6 @@ set_attr:
out:
end_creating(child);
nfsd_attrs_free(&attrs);
-out_write:
fh_drop_write(fhp);
return status;
}