]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
nfsd: fix posix_acl leak on SETACL decode failure
authorJeff Layton <jlayton@kernel.org>
Thu, 21 May 2026 17:51:43 +0000 (13:51 -0400)
committerChuck Lever <cel@kernel.org>
Tue, 9 Jun 2026 20:32:59 +0000 (16:32 -0400)
nfsaclsvc_decode_setaclargs() and nfs3svc_decode_setaclargs() each
call nfs_stream_decode_acl() twice, first for NFS_ACL and then for
NFS_DFACL.  Each successful call transfers ownership of a freshly
allocated posix_acl into argp->acl_access or argp->acl_default.  If
the first call succeeds but the second fails, the decoder returns
false and argp->acl_access is left dangling.

ACLPROC2_SETACL.pc_release was wired to nfssvc_release_attrstat and
ACLPROC3_SETACL.pc_release was wired to nfs3svc_release_fhandle.
Both only call fh_put() and have no knowledge of the ACL fields on
argp.  The posix_acl_release() pairs sat at the out: labels inside
nfsacld_proc_setacl() and nfsd3_proc_setacl(), but svc_process()
skips pc_func when pc_decode returns false, so that cleanup is
unreachable on decode failure:

    svc_process_common()
      pc_decode()                  /* decode_setaclargs: false */
      /* pc_func skipped */
      pc_release()                 /* fh_put only -- ACLs leaked */

The orphaned posix_acl is leaked for the lifetime of the server.

Fix by adding nfsaclsvc_release_setacl() and nfs3svc_release_setacl(),
which release both argp->acl_access and argp->acl_default in addition
to fh_put(), and wiring them as pc_release for their respective SETACL
procedures.  pc_release runs on every path svc_process() takes after
decode, including decode failure, so the posix_acl_release() pairs are
removed from the proc functions' out: labels to keep ownership in one
place.  This matches the existing release_getacl() pattern used by
the sibling GETACL procedures.

Fixes: a257cdd0e217 ("[PATCH] NFSD: Add server support for NFSv3 ACLs.")
Cc: stable@vger.kernel.org
Assisted-by: kres:claude-opus-4-7
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfsd/nfs2acl.c
fs/nfsd/nfs3acl.c

index 0ac538c7618009ae63fbb7de18b6d8237d8d01f8..76305b86c1a95dd7a67d27d5b1ed11a2f04ad0e0 100644 (file)
@@ -131,10 +131,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
        resp->status = fh_getattr(fh, &resp->stat);
 
 out:
-       /* argp->acl_{access,default} may have been allocated in
-          nfssvc_decode_setaclargs. */
-       posix_acl_release(argp->acl_access);
-       posix_acl_release(argp->acl_default);
+       /* argp->acl_{access,default} are released in nfsaclsvc_release_setacl. */
        return rpc_success;
 
 out_drop_lock:
@@ -310,6 +307,16 @@ static void nfsaclsvc_release_access(struct svc_rqst *rqstp)
        fh_put(&resp->fh);
 }
 
+static void nfsaclsvc_release_setacl(struct svc_rqst *rqstp)
+{
+       struct nfsd3_setaclargs *argp = rqstp->rq_argp;
+       struct nfsd_attrstat *resp = rqstp->rq_resp;
+
+       fh_put(&resp->fh);
+       posix_acl_release(argp->acl_access);
+       posix_acl_release(argp->acl_default);
+}
+
 #define ST 1           /* status*/
 #define AT 21          /* attributes */
 #define pAT (1+AT)     /* post attributes - conditional */
@@ -343,7 +350,7 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = {
                .pc_func = nfsacld_proc_setacl,
                .pc_decode = nfsaclsvc_decode_setaclargs,
                .pc_encode = nfssvc_encode_attrstatres,
-               .pc_release = nfssvc_release_attrstat,
+               .pc_release = nfsaclsvc_release_setacl,
                .pc_argsize = sizeof(struct nfsd3_setaclargs),
                .pc_argzero = sizeof(struct nfsd3_setaclargs),
                .pc_ressize = sizeof(struct nfsd_attrstat),
index 7b5433bd3019741dfbf97736724789dcd8d8acd4..e87731380be85cd0686712cae8f42f42b68cb2a9 100644 (file)
@@ -118,10 +118,7 @@ out_drop_lock:
 out_errno:
        resp->status = nfserrno(error);
 out:
-       /* argp->acl_{access,default} may have been allocated in
-          nfs3svc_decode_setaclargs. */
-       posix_acl_release(argp->acl_access);
-       posix_acl_release(argp->acl_default);
+       /* argp->acl_{access,default} are released in nfs3svc_release_setacl. */
        return rpc_success;
 }
 
@@ -223,6 +220,16 @@ static void nfs3svc_release_getacl(struct svc_rqst *rqstp)
        posix_acl_release(resp->acl_default);
 }
 
+static void nfs3svc_release_setacl(struct svc_rqst *rqstp)
+{
+       struct nfsd3_setaclargs *argp = rqstp->rq_argp;
+       struct nfsd3_attrstat *resp = rqstp->rq_resp;
+
+       fh_put(&resp->fh);
+       posix_acl_release(argp->acl_access);
+       posix_acl_release(argp->acl_default);
+}
+
 #define ST 1           /* status*/
 #define AT 21          /* attributes */
 #define pAT (1+AT)     /* post attributes - conditional */
@@ -256,7 +263,7 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = {
                .pc_func = nfsd3_proc_setacl,
                .pc_decode = nfs3svc_decode_setaclargs,
                .pc_encode = nfs3svc_encode_setaclres,
-               .pc_release = nfs3svc_release_fhandle,
+               .pc_release = nfs3svc_release_setacl,
                .pc_argsize = sizeof(struct nfsd3_setaclargs),
                .pc_argzero = sizeof(struct nfsd3_setaclargs),
                .pc_ressize = sizeof(struct nfsd3_attrstat),