Commit 15a8b55d authored by Jeff Layton's avatar Jeff Layton Committed by Chuck Lever
Browse files

nfsd: call op_release, even when op_func returns an error



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: default avatarZhi Li <yieli@redhat.com>
Link: https://bugzilla.redhat.com/show_bug.cgi?id=2181403


Fixes: 34b1744c ("nfsd4: define ->op_release for compound ops")
Signed-off-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent 804d8e0a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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;
}

+5 −6
Original line number Diff line number Diff line
@@ -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);
}

/*