linux-nfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] nfsd: call op_release, even when op_func returns an error
@ 2023-03-27 10:21 Jeff Layton
  2023-03-27 13:14 ` Chuck Lever III
  0 siblings, 1 reply; 4+ messages in thread
From: Jeff Layton @ 2023-03-27 10:21 UTC (permalink / raw)
  To: chuck.lever; +Cc: linux-nfs, Zhi Li

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
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/nfsd/blocklayout.c |  1 +
 fs/nfsd/nfs4xdr.c     | 13 +++++++------
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
index 04697f8dc37d..01d7fd108cf3 100644
--- a/fs/nfsd/blocklayout.c
+++ b/fs/nfsd/blocklayout.c
@@ -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;
 }
 
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index e12e5a4ad502..6b675fbdabd0 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -5402,7 +5402,7 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
 	p = xdr_reserve_space(xdr, 8);
 	if (!p) {
 		WARN_ON_ONCE(1);
-		return;
+		goto release;
 	}
 	*p++ = cpu_to_be32(op->opnum);
 	post_err_offset = xdr->buf->len;
@@ -5418,8 +5418,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,11 +5458,14 @@ 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);
 }
 
-/* 
- * Encode the reply stored in the stateowner reply cache 
- * 
+/*
+ * Encode the reply stored in the stateowner reply cache
+ *
  * XDR note: do not encode rp->rp_buflen: the buffer contains the
  * previously sent already encoded operation.
  */
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] nfsd: call op_release, even when op_func returns an error
  2023-03-27 10:21 [PATCH] nfsd: call op_release, even when op_func returns an error Jeff Layton
@ 2023-03-27 13:14 ` Chuck Lever III
  2023-03-27 14:32   ` Jeff Layton
  2023-03-30 18:15   ` Jeff Layton
  0 siblings, 2 replies; 4+ messages in thread
From: Chuck Lever III @ 2023-03-27 13:14 UTC (permalink / raw)
  To: Jeff Layton; +Cc: Linux NFS Mailing List, Zhi Li



> On Mar 27, 2023, at 6:21 AM, Jeff Layton <jlayton@kernel.org> wrote:
> 
> 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
> Signed-off-by: Jeff Layton <jlayton@kernel.org>

Thanks, Jeff.

May I add: Fixes: 34b1744c91cc ("nfsd4: define ->op_release for
compound ops") ?


> ---
> fs/nfsd/blocklayout.c |  1 +
> fs/nfsd/nfs4xdr.c     | 13 +++++++------
> 2 files changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
> index 04697f8dc37d..01d7fd108cf3 100644
> --- a/fs/nfsd/blocklayout.c
> +++ b/fs/nfsd/blocklayout.c
> @@ -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;
> }
> 
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index e12e5a4ad502..6b675fbdabd0 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -5402,7 +5402,7 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
> 	p = xdr_reserve_space(xdr, 8);
> 	if (!p) {
> 		WARN_ON_ONCE(1);
> -		return;
> +		goto release;
> 	}
> 	*p++ = cpu_to_be32(op->opnum);
> 	post_err_offset = xdr->buf->len;
> @@ -5418,8 +5418,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,11 +5458,14 @@ 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);
> }
> 
> -/* 
> - * Encode the reply stored in the stateowner reply cache 
> - * 
> +/*
> + * Encode the reply stored in the stateowner reply cache
> + *
>  * XDR note: do not encode rp->rp_buflen: the buffer contains the
>  * previously sent already encoded operation.
>  */
> -- 
> 2.39.2
> 

--
Chuck Lever



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] nfsd: call op_release, even when op_func returns an error
  2023-03-27 13:14 ` Chuck Lever III
@ 2023-03-27 14:32   ` Jeff Layton
  2023-03-30 18:15   ` Jeff Layton
  1 sibling, 0 replies; 4+ messages in thread
From: Jeff Layton @ 2023-03-27 14:32 UTC (permalink / raw)
  To: Chuck Lever III; +Cc: Linux NFS Mailing List, Zhi Li

On Mon, 2023-03-27 at 13:14 +0000, Chuck Lever III wrote:
> 
> > On Mar 27, 2023, at 6:21 AM, Jeff Layton <jlayton@kernel.org> wrote:
> > 
> > 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
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> 
> Thanks, Jeff.
> 
> May I add: Fixes: 34b1744c91cc ("nfsd4: define ->op_release for
> compound ops") ?
> 
> 

Sure. It does look like the leaks stretch back at least that far.
 
> > ---
> > fs/nfsd/blocklayout.c |  1 +
> > fs/nfsd/nfs4xdr.c     | 13 +++++++------
> > 2 files changed, 8 insertions(+), 6 deletions(-)
> > 
> > diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
> > index 04697f8dc37d..01d7fd108cf3 100644
> > --- a/fs/nfsd/blocklayout.c
> > +++ b/fs/nfsd/blocklayout.c
> > @@ -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;
> > }
> > 
> > diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> > index e12e5a4ad502..6b675fbdabd0 100644
> > --- a/fs/nfsd/nfs4xdr.c
> > +++ b/fs/nfsd/nfs4xdr.c
> > @@ -5402,7 +5402,7 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
> > 	p = xdr_reserve_space(xdr, 8);
> > 	if (!p) {
> > 		WARN_ON_ONCE(1);
> > -		return;
> > +		goto release;
> > 	}
> > 	*p++ = cpu_to_be32(op->opnum);
> > 	post_err_offset = xdr->buf->len;
> > @@ -5418,8 +5418,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,11 +5458,14 @@ 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);
> > }
> > 
> > -/* 
> > - * Encode the reply stored in the stateowner reply cache 
> > - * 
> > +/*
> > + * Encode the reply stored in the stateowner reply cache
> > + *
> >  * XDR note: do not encode rp->rp_buflen: the buffer contains the
> >  * previously sent already encoded operation.
> >  */
> > -- 
> > 2.39.2
> > 
> 
> --
> Chuck Lever
> 
> 

-- 
Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] nfsd: call op_release, even when op_func returns an error
  2023-03-27 13:14 ` Chuck Lever III
  2023-03-27 14:32   ` Jeff Layton
@ 2023-03-30 18:15   ` Jeff Layton
  1 sibling, 0 replies; 4+ messages in thread
From: Jeff Layton @ 2023-03-30 18:15 UTC (permalink / raw)
  To: Chuck Lever III; +Cc: Linux NFS Mailing List, Zhi Li

On Mon, 2023-03-27 at 13:14 +0000, Chuck Lever III wrote:
> 
> > On Mar 27, 2023, at 6:21 AM, Jeff Layton <jlayton@kernel.org> wrote:
> > 
> > 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
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> 
> Thanks, Jeff.
> 
> May I add: Fixes: 34b1744c91cc ("nfsd4: define ->op_release for
> compound ops") ?
> 

I've seen some problems with this patch in testing and I have a fix
forthcoming (once I finish testing it):

The root cause is the OPDESC() function which can walk off the end of
the nfsd4_ops array when passed a large value (like OP_ILLEGAL). I think
we'll want to fix that to do something more sane before merging this
patch.

> 
> > ---
> > fs/nfsd/blocklayout.c |  1 +
> > fs/nfsd/nfs4xdr.c     | 13 +++++++------
> > 2 files changed, 8 insertions(+), 6 deletions(-)
> > 
> > diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
> > index 04697f8dc37d..01d7fd108cf3 100644
> > --- a/fs/nfsd/blocklayout.c
> > +++ b/fs/nfsd/blocklayout.c
> > @@ -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;
> > }
> > 
> > diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> > index e12e5a4ad502..6b675fbdabd0 100644
> > --- a/fs/nfsd/nfs4xdr.c
> > +++ b/fs/nfsd/nfs4xdr.c
> > @@ -5402,7 +5402,7 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
> > 	p = xdr_reserve_space(xdr, 8);
> > 	if (!p) {
> > 		WARN_ON_ONCE(1);
> > -		return;
> > +		goto release;
> > 	}
> > 	*p++ = cpu_to_be32(op->opnum);
> > 	post_err_offset = xdr->buf->len;
> > @@ -5418,8 +5418,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,11 +5458,14 @@ 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);
> > }
> > 
> > -/* 
> > - * Encode the reply stored in the stateowner reply cache 
> > - * 
> > +/*
> > + * Encode the reply stored in the stateowner reply cache
> > + *
> >  * XDR note: do not encode rp->rp_buflen: the buffer contains the
> >  * previously sent already encoded operation.
> >  */
> > -- 
> > 2.39.2
> > 
> 
> --
> Chuck Lever
> 
> 

-- 
Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2023-03-30 18:15 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-27 10:21 [PATCH] nfsd: call op_release, even when op_func returns an error Jeff Layton
2023-03-27 13:14 ` Chuck Lever III
2023-03-27 14:32   ` Jeff Layton
2023-03-30 18:15   ` Jeff Layton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).