diff mbox series

[v1] SUNRPC: Fix READ_PLUS crasher

Message ID 165660978413.2453.15153844664543877314.stgit@klimt.1015granger.net (mailing list archive)
State New, archived
Headers show
Series [v1] SUNRPC: Fix READ_PLUS crasher | expand

Commit Message

Chuck Lever June 30, 2022, 5:24 p.m. UTC
Looks like there are still cases when "space_left - frag1bytes" can
legitimately exceed PAGE_SIZE. Ensure that xdr->end always remains
within the current encode buffer.

Reported-by: Bruce Fields <bfields@fieldses.org>
Reported-by: Zorro Lang <zlang@redhat.com>
Link: https://bugzilla.kernel.org/show_bug.cgi?id=216151
Fixes: 6c254bf3b637 ("SUNRPC: Fix the calculation of xdr->end in xdr_get_next_encode_buffer()")
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/xdr.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Hi-

I had a few minutes yesterday afternoon to look into this one. The
following one-liner seems to address the issue and passes my smoke
tests with NFSv4.1/TCP and NFSv3/RDMA. Any thoughts?

Comments

Trond Myklebust June 30, 2022, 6 p.m. UTC | #1
On Thu, 2022-06-30 at 13:24 -0400, Chuck Lever wrote:
> Looks like there are still cases when "space_left - frag1bytes" can
> legitimately exceed PAGE_SIZE. Ensure that xdr->end always remains
> within the current encode buffer.
> 
> Reported-by: Bruce Fields <bfields@fieldses.org>
> Reported-by: Zorro Lang <zlang@redhat.com>
> Link: https://bugzilla.kernel.org/show_bug.cgi?id=216151
> Fixes: 6c254bf3b637 ("SUNRPC: Fix the calculation of xdr->end in
> xdr_get_next_encode_buffer()")
> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> ---
>  net/sunrpc/xdr.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> Hi-
> 
> I had a few minutes yesterday afternoon to look into this one. The
> following one-liner seems to address the issue and passes my smoke
> tests with NFSv4.1/TCP and NFSv3/RDMA. Any thoughts?
> 
> 
> diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
> index f87a2d8f23a7..916659be2774 100644
> --- a/net/sunrpc/xdr.c
> +++ b/net/sunrpc/xdr.c
> @@ -987,7 +987,7 @@ static noinline __be32
> *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
>         if (space_left - nbytes >= PAGE_SIZE)
>                 xdr->end = p + PAGE_SIZE;
>         else
> -               xdr->end = p + space_left - frag1bytes;
> +               xdr->end = p + min_t(int, space_left - frag1bytes,
> PAGE_SIZE);

Since we know that frag1bytes <= nbytes (that is determined in
xdr_reserve_space()), isn't this effectively the same thing as changing
the condition to

	if (space_left - frag1bytes >= PAGE_SIZE)
		xdr->end = p + PAGE_SIZE;
	else
		xdr->end = p + space_left - frag1bytes;

?
>  
>         xdr->buf->page_len += frag2bytes;
>         xdr->buf->len += nbytes;
> 
>
Anna Schumaker June 30, 2022, 6:09 p.m. UTC | #2
On Thu, Jun 30, 2022 at 2:03 PM Trond Myklebust <trondmy@hammerspace.com> wrote:
>
> On Thu, 2022-06-30 at 13:24 -0400, Chuck Lever wrote:
> > Looks like there are still cases when "space_left - frag1bytes" can
> > legitimately exceed PAGE_SIZE. Ensure that xdr->end always remains
> > within the current encode buffer.
> >
> > Reported-by: Bruce Fields <bfields@fieldses.org>
> > Reported-by: Zorro Lang <zlang@redhat.com>
> > Link: https://bugzilla.kernel.org/show_bug.cgi?id=216151
> > Fixes: 6c254bf3b637 ("SUNRPC: Fix the calculation of xdr->end in
> > xdr_get_next_encode_buffer()")
> > Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> > ---
> >  net/sunrpc/xdr.c |    2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > Hi-
> >
> > I had a few minutes yesterday afternoon to look into this one. The
> > following one-liner seems to address the issue and passes my smoke
> > tests with NFSv4.1/TCP and NFSv3/RDMA. Any thoughts?
> >
> >
> > diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
> > index f87a2d8f23a7..916659be2774 100644
> > --- a/net/sunrpc/xdr.c
> > +++ b/net/sunrpc/xdr.c
> > @@ -987,7 +987,7 @@ static noinline __be32
> > *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
> >         if (space_left - nbytes >= PAGE_SIZE)
> >                 xdr->end = p + PAGE_SIZE;
> >         else
> > -               xdr->end = p + space_left - frag1bytes;
> > +               xdr->end = p + min_t(int, space_left - frag1bytes,
> > PAGE_SIZE);
>
> Since we know that frag1bytes <= nbytes (that is determined in
> xdr_reserve_space()), isn't this effectively the same thing as changing
> the condition to
>
>         if (space_left - frag1bytes >= PAGE_SIZE)
>                 xdr->end = p + PAGE_SIZE;
>         else
>                 xdr->end = p + space_left - frag1bytes;

I added some printk's without this patch, and I'm seeing space_left
larger than PAGE_SIZE and frag1bytes set to 0 in that branch right
before the crash. So the min_t() will choose the PAGE_SIZE option
sometimes.

Anna

>
> ?
> >
> >         xdr->buf->page_len += frag2bytes;
> >         xdr->buf->len += nbytes;
> >
> >
>
> --
> Trond Myklebust
> Linux NFS client maintainer, Hammerspace
> trond.myklebust@hammerspace.com
>
>
Anna Schumaker June 30, 2022, 6:31 p.m. UTC | #3
On Thu, Jun 30, 2022 at 2:09 PM Anna Schumaker <schumaker.anna@gmail.com> wrote:
>
> On Thu, Jun 30, 2022 at 2:03 PM Trond Myklebust <trondmy@hammerspace.com> wrote:
> >
> > On Thu, 2022-06-30 at 13:24 -0400, Chuck Lever wrote:
> > > Looks like there are still cases when "space_left - frag1bytes" can
> > > legitimately exceed PAGE_SIZE. Ensure that xdr->end always remains
> > > within the current encode buffer.
> > >
> > > Reported-by: Bruce Fields <bfields@fieldses.org>
> > > Reported-by: Zorro Lang <zlang@redhat.com>
> > > Link: https://bugzilla.kernel.org/show_bug.cgi?id=216151
> > > Fixes: 6c254bf3b637 ("SUNRPC: Fix the calculation of xdr->end in
> > > xdr_get_next_encode_buffer()")
> > > Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> > > ---
> > >  net/sunrpc/xdr.c |    2 +-
> > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > Hi-
> > >
> > > I had a few minutes yesterday afternoon to look into this one. The
> > > following one-liner seems to address the issue and passes my smoke
> > > tests with NFSv4.1/TCP and NFSv3/RDMA. Any thoughts?
> > >
> > >
> > > diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
> > > index f87a2d8f23a7..916659be2774 100644
> > > --- a/net/sunrpc/xdr.c
> > > +++ b/net/sunrpc/xdr.c
> > > @@ -987,7 +987,7 @@ static noinline __be32
> > > *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
> > >         if (space_left - nbytes >= PAGE_SIZE)
> > >                 xdr->end = p + PAGE_SIZE;
> > >         else
> > > -               xdr->end = p + space_left - frag1bytes;
> > > +               xdr->end = p + min_t(int, space_left - frag1bytes,
> > > PAGE_SIZE);
> >
> > Since we know that frag1bytes <= nbytes (that is determined in
> > xdr_reserve_space()), isn't this effectively the same thing as changing
> > the condition to
> >
> >         if (space_left - frag1bytes >= PAGE_SIZE)
> >                 xdr->end = p + PAGE_SIZE;
> >         else
> >                 xdr->end = p + space_left - frag1bytes;
>
> I added some printk's without this patch, and I'm seeing space_left
> larger than PAGE_SIZE and frag1bytes set to 0 in that branch right
> before the crash. So the min_t() will choose the PAGE_SIZE option
> sometimes.

Actually, ignore me. I initially misread what Trond said.  After a
reread I saw he changed "space_left - nbytes" to "space_left -
frag1bytes" in the if condition. Trond's way fixes the crash for me,
too.

Anna
>
> Anna
>
> >
> > ?
> > >
> > >         xdr->buf->page_len += frag2bytes;
> > >         xdr->buf->len += nbytes;
> > >
> > >
> >
> > --
> > Trond Myklebust
> > Linux NFS client maintainer, Hammerspace
> > trond.myklebust@hammerspace.com
> >
> >
Trond Myklebust June 30, 2022, 6:40 p.m. UTC | #4
On Thu, 2022-06-30 at 14:09 -0400, Anna Schumaker wrote:
> On Thu, Jun 30, 2022 at 2:03 PM Trond Myklebust
> <trondmy@hammerspace.com> wrote:
> > 
> > On Thu, 2022-06-30 at 13:24 -0400, Chuck Lever wrote:
> > > Looks like there are still cases when "space_left - frag1bytes"
> > > can
> > > legitimately exceed PAGE_SIZE. Ensure that xdr->end always
> > > remains
> > > within the current encode buffer.
> > > 
> > > Reported-by: Bruce Fields <bfields@fieldses.org>
> > > Reported-by: Zorro Lang <zlang@redhat.com>
> > > Link: https://bugzilla.kernel.org/show_bug.cgi?id=216151
> > > Fixes: 6c254bf3b637 ("SUNRPC: Fix the calculation of xdr->end in
> > > xdr_get_next_encode_buffer()")
> > > Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> > > ---
> > >  net/sunrpc/xdr.c |    2 +-
> > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > > 
> > > Hi-
> > > 
> > > I had a few minutes yesterday afternoon to look into this one.
> > > The
> > > following one-liner seems to address the issue and passes my
> > > smoke
> > > tests with NFSv4.1/TCP and NFSv3/RDMA. Any thoughts?
> > > 
> > > 
> > > diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
> > > index f87a2d8f23a7..916659be2774 100644
> > > --- a/net/sunrpc/xdr.c
> > > +++ b/net/sunrpc/xdr.c
> > > @@ -987,7 +987,7 @@ static noinline __be32
> > > *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
> > >         if (space_left - nbytes >= PAGE_SIZE)
> > >                 xdr->end = p + PAGE_SIZE;
> > >         else
> > > -               xdr->end = p + space_left - frag1bytes;
> > > +               xdr->end = p + min_t(int, space_left -
> > > frag1bytes,
> > > PAGE_SIZE);
> > 
> > Since we know that frag1bytes <= nbytes (that is determined in
> > xdr_reserve_space()), isn't this effectively the same thing as
> > changing
> > the condition to
> > 
> >         if (space_left - frag1bytes >= PAGE_SIZE)
> >                 xdr->end = p + PAGE_SIZE;
> >         else
> >                 xdr->end = p + space_left - frag1bytes;
> 
> I added some printk's without this patch, and I'm seeing space_left
> larger than PAGE_SIZE and frag1bytes set to 0 in that branch right
> before the crash. So the min_t() will choose the PAGE_SIZE option
> sometimes.
> 

Sure. My point is that it makes the test for space_left - nbytes
redundant (and confusing).

>
Chuck Lever June 30, 2022, 6:45 p.m. UTC | #5
> On Jun 30, 2022, at 2:40 PM, Trond Myklebust <trondmy@hammerspace.com> wrote:
> 
> On Thu, 2022-06-30 at 14:09 -0400, Anna Schumaker wrote:
>> On Thu, Jun 30, 2022 at 2:03 PM Trond Myklebust
>> <trondmy@hammerspace.com> wrote:
>>> 
>>> On Thu, 2022-06-30 at 13:24 -0400, Chuck Lever wrote:
>>>> Looks like there are still cases when "space_left - frag1bytes"
>>>> can
>>>> legitimately exceed PAGE_SIZE. Ensure that xdr->end always
>>>> remains
>>>> within the current encode buffer.
>>>> 
>>>> Reported-by: Bruce Fields <bfields@fieldses.org>
>>>> Reported-by: Zorro Lang <zlang@redhat.com>
>>>> Link: https://bugzilla.kernel.org/show_bug.cgi?id=216151
>>>> Fixes: 6c254bf3b637 ("SUNRPC: Fix the calculation of xdr->end in
>>>> xdr_get_next_encode_buffer()")
>>>> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
>>>> ---
>>>> net/sunrpc/xdr.c |  2 +-
>>>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>>> 
>>>> Hi-
>>>> 
>>>> I had a few minutes yesterday afternoon to look into this one.
>>>> The
>>>> following one-liner seems to address the issue and passes my
>>>> smoke
>>>> tests with NFSv4.1/TCP and NFSv3/RDMA. Any thoughts?
>>>> 
>>>> 
>>>> diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
>>>> index f87a2d8f23a7..916659be2774 100644
>>>> --- a/net/sunrpc/xdr.c
>>>> +++ b/net/sunrpc/xdr.c
>>>> @@ -987,7 +987,7 @@ static noinline __be32
>>>> *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
>>>> if (space_left - nbytes >= PAGE_SIZE)
>>>> xdr->end = p + PAGE_SIZE;
>>>> else
>>>> -  xdr->end = p + space_left - frag1bytes;
>>>> +  xdr->end = p + min_t(int, space_left -
>>>> frag1bytes,
>>>> PAGE_SIZE);
>>> 
>>> Since we know that frag1bytes <= nbytes (that is determined in
>>> xdr_reserve_space()), isn't this effectively the same thing as
>>> changing
>>> the condition to
>>> 
>>> if (space_left - frag1bytes >= PAGE_SIZE)
>>> xdr->end = p + PAGE_SIZE;
>>> else
>>> xdr->end = p + space_left - frag1bytes;
>> 
>> I added some printk's without this patch, and I'm seeing space_left
>> larger than PAGE_SIZE and frag1bytes set to 0 in that branch right
>> before the crash. So the min_t() will choose the PAGE_SIZE option
>> sometimes.
>> 
> 
> Sure. My point is that it makes the test for space_left - nbytes
> redundant (and confusing).

I didn't immediately see how to collapse the checks together, as
I had tried something like that while working on 6c254bf3b637.
But I will try out your suggestion. If it tests OK, I will post
a v2 of this patch.


--
Chuck Lever
J. Bruce Fields June 30, 2022, 9:12 p.m. UTC | #6
Looks like the crash I saw no longer reproduces after that, fwiw.

--b.

On Thu, Jun 30, 2022 at 01:24:20PM -0400, Chuck Lever wrote:
> Looks like there are still cases when "space_left - frag1bytes" can
> legitimately exceed PAGE_SIZE. Ensure that xdr->end always remains
> within the current encode buffer.
> 
> Reported-by: Bruce Fields <bfields@fieldses.org>
> Reported-by: Zorro Lang <zlang@redhat.com>
> Link: https://bugzilla.kernel.org/show_bug.cgi?id=216151
> Fixes: 6c254bf3b637 ("SUNRPC: Fix the calculation of xdr->end in xdr_get_next_encode_buffer()")
> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> ---
>  net/sunrpc/xdr.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> Hi-
> 
> I had a few minutes yesterday afternoon to look into this one. The
> following one-liner seems to address the issue and passes my smoke
> tests with NFSv4.1/TCP and NFSv3/RDMA. Any thoughts?
> 
> 
> diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
> index f87a2d8f23a7..916659be2774 100644
> --- a/net/sunrpc/xdr.c
> +++ b/net/sunrpc/xdr.c
> @@ -987,7 +987,7 @@ static noinline __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
>  	if (space_left - nbytes >= PAGE_SIZE)
>  		xdr->end = p + PAGE_SIZE;
>  	else
> -		xdr->end = p + space_left - frag1bytes;
> +		xdr->end = p + min_t(int, space_left - frag1bytes, PAGE_SIZE);
>  
>  	xdr->buf->page_len += frag2bytes;
>  	xdr->buf->len += nbytes;
>
diff mbox series

Patch

diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index f87a2d8f23a7..916659be2774 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -987,7 +987,7 @@  static noinline __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
 	if (space_left - nbytes >= PAGE_SIZE)
 		xdr->end = p + PAGE_SIZE;
 	else
-		xdr->end = p + space_left - frag1bytes;
+		xdr->end = p + min_t(int, space_left - frag1bytes, PAGE_SIZE);
 
 	xdr->buf->page_len += frag2bytes;
 	xdr->buf->len += nbytes;