diff options
| author | Jeff Layton <jlayton@kernel.org> | 2026-05-21 19:37:33 +0300 |
|---|---|---|
| committer | Chuck Lever <cel@kernel.org> | 2026-06-09 23:32:59 +0300 |
| commit | 24c975bbdd564d7d0ad90294bfa69729830345de (patch) | |
| tree | e2c733d7e862e412f63972f95acf6daf3c79bfd1 | |
| parent | e186fa1c057f5eccb22afb1e83e34c0627085868 (diff) | |
| download | linux-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.c | 15 |
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; } |
