]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
nfsd: fix posix_acl leak and ignored error in nfsd4_create_file
authorJeff Layton <jlayton@kernel.org>
Thu, 21 May 2026 16:37:33 +0000 (12:37 -0400)
committerChuck Lever <cel@kernel.org>
Tue, 9 Jun 2026 20:32:59 +0000 (16:32 -0400)
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>
fs/nfsd/nfs4proc.c

index 14e329cfdfa621c041dc0813671bfd7e0fdf75ad..8561540ab2db98a890b7a40a207a9f5a96089a50 100644 (file)
@@ -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;
 }