nfsd: call op_release, even when op_func returns an error
authorJeff Layton <jlayton@kernel.org>
Mon, 27 Mar 2023 10:21:37 +0000 (06:21 -0400)
committerChuck Lever <chuck.lever@oracle.com>
Fri, 31 Mar 2023 21:29:49 +0000 (17:29 -0400)
For ops with "trivial" replies, nfsd4_encode_operation will shortcut
most of the encoding work and skip to just marshalling up the status.
One of the things it skips is calling op_release. This could cause a
memory leak in the layoutget codepath if there is an error at an
inopportune time.

Have the compound processing engine always call op_release, even when
op_func sets an error in op->status. With this change, we also need
nfsd4_block_get_device_info_scsi to set the gd_device pointer to NULL
on error to avoid a double free.

Reported-by: Zhi Li <yieli@redhat.com>
Link: https://bugzilla.redhat.com/show_bug.cgi?id=2181403
Fixes: 34b1744c91cc ("nfsd4: define ->op_release for compound ops")
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfsd/blocklayout.c
fs/nfsd/nfs4xdr.c

index 04697f8dc37d6a6d8e3e28a2f423d80619322759..01d7fd108cf3df6bd1abdd050c4a75c14b34fd3d 100644 (file)
@@ -297,6 +297,7 @@ nfsd4_block_get_device_info_scsi(struct super_block *sb,
 
 out_free_dev:
        kfree(dev);
+       gdp->gd_device = NULL;
        return ret;
 }
 
index 67bbd2d6334c4f08698734fc8397e24e403160c7..7799835c2196e57f9dc283785d7827f0da9cced0 100644 (file)
@@ -5400,10 +5400,8 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
        __be32 *p;
 
        p = xdr_reserve_space(xdr, 8);
-       if (!p) {
-               WARN_ON_ONCE(1);
-               return;
-       }
+       if (!p)
+               goto release;
        *p++ = cpu_to_be32(op->opnum);
        post_err_offset = xdr->buf->len;
 
@@ -5418,8 +5416,6 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
        op->status = encoder(resp, op->status, &op->u);
        if (op->status)
                trace_nfsd_compound_encode_err(rqstp, op->opnum, op->status);
-       if (opdesc && opdesc->op_release)
-               opdesc->op_release(&op->u);
        xdr_commit_encode(xdr);
 
        /* nfsd4_check_resp_size guarantees enough room for error status */
@@ -5460,6 +5456,9 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
        }
 status:
        *p = op->status;
+release:
+       if (opdesc && opdesc->op_release)
+               opdesc->op_release(&op->u);
 }
 
 /*