Re: [PATCH v2 1/2] SUNRPC: Fix buffer handling of GSS MIC without slack

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, 2019-09-15 at 12:43 -0400, Chuck Lever wrote:
> Hi Ben-
> 
> 
> > On Sep 15, 2019, at 11:41 AM, Benjamin Coddington <
> > bcodding@xxxxxxxxxx> wrote:
> > 
> > The GSS Message Integrity Check data for krb5i may lie partially in
> > the XDR
> > reply buffer's pages and tail.  If so, we try to copy the entire
> > MIC into
> > free space in the tail.  But as the estimations of the slack space
> > required
> > for authentication and verification have improved there may be less
> > free
> > space in the tail to complete this copy -- see commit 2c94b8eca1a2
> > ("SUNRPC: Use au_rslack when computing reply buffer size").  In
> > fact, there
> > may only be room in the tail for a single copy of the MIC, and not
> > part of
> > the MIC and then another complete copy.
> > 
> > The real world failure reported is that `ls` of a directory on NFS
> > may
> > sometimes return -EIO, which can be traced back to
> > xdr_buf_read_netobj()
> > failing to find available free space in the tail to copy the MIC.
> > 
> > Fix this by checking for the case of the MIC crossing the
> > boundaries of
> > head, pages, and tail. If so, shift the buffer until the MIC is
> > contained
> > completely within the pages or tail.  This allows the remainder of
> > the
> > function to create a sub buffer that directly address the complete
> > MIC.
> > 
> > Signed-off-by: Benjamin Coddington <bcodding@xxxxxxxxxx>
> > Cc: stable@xxxxxxxxxxxxxxx # v5.1
> > ---
> > net/sunrpc/xdr.c | 32 +++++++++++++++++++-------------
> > 1 file changed, 19 insertions(+), 13 deletions(-)
> > 
> > diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
> > index 48c93b9e525e..a29ce73c3029 100644
> > --- a/net/sunrpc/xdr.c
> > +++ b/net/sunrpc/xdr.c
> > @@ -1237,16 +1237,29 @@ xdr_encode_word(struct xdr_buf *buf,
> > unsigned int base, u32 obj)
> > EXPORT_SYMBOL_GPL(xdr_encode_word);
> > 
> > /* If the netobj starting offset bytes from the start of xdr_buf is
> > contained
> > - * entirely in the head or the tail, set object to point to it;
> > otherwise
> > - * try to find space for it at the end of the tail, copy it there,
> > and
> > - * set obj to point to it. */
> > + * entirely in the head, pages, or tail, set object to point to
> > it; otherwise
> > + * shift the buffer until it is contained entirely within the
> > pages or tail.
> > + */
> > int xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj
> > *obj, unsigned int offset)
> > {
> > 	struct xdr_buf subbuf;
> > +	unsigned int boundary;
> > 
> > 	if (xdr_decode_word(buf, offset, &obj->len))
> > 		return -EFAULT;
> > -	if (xdr_buf_subsegment(buf, &subbuf, offset + 4, obj->len))
> > +	offset += 4;
> > +
> > +	/* Is the obj partially in the head? */
> > +	boundary = buf->head[0].iov_len;
> > +	if (offset < boundary && (offset + obj->len) > boundary)
> > +		xdr_shift_buf(buf, boundary - offset);
> > +
> > +	/* Is the obj partially in the pages? */
> > +	boundary += buf->page_len;
> > +	if (offset < boundary && (offset + obj->len) > boundary)
> > +		xdr_shrink_pagelen(buf, boundary - offset);
> > +
> > +	if (xdr_buf_subsegment(buf, &subbuf, offset, obj->len))
> > 		return -EFAULT;
> > 
> > 	/* Is the obj contained entirely in the head? */
> > @@ -1258,17 +1271,10 @@ int xdr_buf_read_netobj(struct xdr_buf
> > *buf, struct xdr_netobj *obj, unsigned in
> > 	if (subbuf.tail[0].iov_len == obj->len)
> > 		return 0;
> > 
> > -	/* use end of tail as storage for obj:
> > -	 * (We don't copy to the beginning because then we'd have
> > -	 * to worry about doing a potentially overlapping copy.
> > -	 * This assumes the object is at most half the length of the
> > -	 * tail.) */
> > +	/* obj is in the pages: move to end of the tail */
> 
> How about "/* Find a contiguous area in @buf to hold all of @obj */"
> ?
> 
> 
> > 	if (obj->len > buf->buflen - buf->len)
> > 		return -ENOMEM;
> > -	if (buf->tail[0].iov_len != 0)
> > -		obj->data = buf->tail[0].iov_base + buf-
> > >tail[0].iov_len;
> > -	else
> > -		obj->data = buf->head[0].iov_base + buf-
> > >head[0].iov_len;
> > +	obj->data = buf->tail[0].iov_base + buf->tail[0].iov_len;
> 
> Your new code assumes that when krb5i is in use, the upper layer will
> always
> provide a non-NULL tail->iov_len. I wouldn't swear that will always
> be true:
> The reason for the "if (buf->tail[0].iov_len)" check is to see
> whether the
> upper layer indeed has set up a tail. iov_len will be non-zero only
> when the
> upper layer has provided a tail buffer.

For the case where we only have kvec data, then xdr_buf_init() assigns
all the allocated buffer to the header, so the mic should be
contiguously buffered there.

If we have page data, then the fact that auth->au_rslack > auth-
>au_ralign should ensure that we have allocated a sufficiently large
tail buffer (see rpc_prepare_reply_pages()).

BTW: this also means we should really only need to move the mic in the
case where buf->page_len != 0.
It also means that we should probably change xdr_inline_pages() to be a
static function, since the assumption above fails if the user does not
call rpc_prepare_reply_pages() in order to calculate the header
alignment correctly.

-- 
Trond Myklebust
Linux NFS client maintainer, Hammerspace
trond.myklebust@xxxxxxxxxxxxxxx






[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux