From 76cacaabf15a593833d96a65a1a251002bd88178 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 26 Jun 2012 15:32:40 -0400 Subject: SUNRPC: xdr_read_pages needs to clear xdr->page_ptr. Signed-off-by: Trond Myklebust --- net/sunrpc/xdr.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/sunrpc/xdr.c') diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index fddcccfcdf76..539c19fb91de 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -773,6 +773,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) */ xdr->p = (__be32 *)((char *)iov->iov_base + padding); xdr->end = (__be32 *)((char *)iov->iov_base + end); + xdr->page_ptr = NULL; } EXPORT_SYMBOL_GPL(xdr_read_pages); -- cgit v1.2.3 From 1537693ceaa8d6484f1ce631bec85658bfa9816c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 28 Jun 2012 17:17:48 -0400 Subject: SUNRPC: Clean up xdr_set_iov() Remove the 'p' argument, since that is only ever set by xdr_init_decode. Add sanity checking of 'p' inside xdr_init_decode itself. Signed-off-by: Trond Myklebust --- net/sunrpc/xdr.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'net/sunrpc/xdr.c') diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 539c19fb91de..21d041cf7f65 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -554,13 +554,11 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int b EXPORT_SYMBOL_GPL(xdr_write_pages); static void xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov, - __be32 *p, unsigned int len) + unsigned int len) { if (len > iov->iov_len) len = iov->iov_len; - if (p == NULL) - p = (__be32*)iov->iov_base; - xdr->p = p; + xdr->p = (__be32*)iov->iov_base; xdr->end = (__be32*)(iov->iov_base + len); xdr->iov = iov; xdr->page_ptr = NULL; @@ -607,7 +605,7 @@ static void xdr_set_next_page(struct xdr_stream *xdr) newbase -= xdr->buf->page_base; if (xdr_set_page_base(xdr, newbase, PAGE_SIZE) < 0) - xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len); + xdr_set_iov(xdr, xdr->buf->tail, xdr->buf->len); } static bool xdr_set_next_buffer(struct xdr_stream *xdr) @@ -616,7 +614,7 @@ static bool xdr_set_next_buffer(struct xdr_stream *xdr) xdr_set_next_page(xdr); else if (xdr->iov == xdr->buf->head) { if (xdr_set_page_base(xdr, 0, PAGE_SIZE) < 0) - xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len); + xdr_set_iov(xdr, xdr->buf->tail, xdr->buf->len); } return xdr->p != xdr->end; } @@ -633,9 +631,11 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) xdr->scratch.iov_base = NULL; xdr->scratch.iov_len = 0; if (buf->head[0].iov_len != 0) - xdr_set_iov(xdr, buf->head, p, buf->len); + xdr_set_iov(xdr, buf->head, buf->len); else if (buf->page_len != 0) xdr_set_page_base(xdr, 0, buf->len); + if (p != NULL && p > xdr->p && xdr->end >= p) + xdr->p = p; } EXPORT_SYMBOL_GPL(xdr_init_decode); -- cgit v1.2.3 From bfeea1dc1c9238f9e5a17a265489ecd0d19f66bb Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 20 Jun 2012 09:58:35 -0400 Subject: SUNRPC: Don't decode beyond the end of the RPC reply message Now that xdr_inline_decode() will automatically cross into the page buffers, we need to ensure that it doesn't exceed the total reply message length. This patch sets up a counter that tracks the number of words remaining in the reply message, and ensures that xdr_inline_decode, xdr_read_pages and xdr_enter_page respect the end of message boundary. Signed-off-by: Trond Myklebust --- net/sunrpc/xdr.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'net/sunrpc/xdr.c') diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 21d041cf7f65..5643feb6c645 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -630,12 +630,15 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) xdr->buf = buf; xdr->scratch.iov_base = NULL; xdr->scratch.iov_len = 0; + xdr->nwords = XDR_QUADLEN(buf->len); if (buf->head[0].iov_len != 0) xdr_set_iov(xdr, buf->head, buf->len); else if (buf->page_len != 0) xdr_set_page_base(xdr, 0, buf->len); - if (p != NULL && p > xdr->p && xdr->end >= p) + if (p != NULL && p > xdr->p && xdr->end >= p) { + xdr->nwords -= p - xdr->p; xdr->p = p; + } } EXPORT_SYMBOL_GPL(xdr_init_decode); @@ -660,12 +663,14 @@ EXPORT_SYMBOL_GPL(xdr_init_decode_pages); static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) { + unsigned int nwords = XDR_QUADLEN(nbytes); __be32 *p = xdr->p; - __be32 *q = p + XDR_QUADLEN(nbytes); + __be32 *q = p + nwords; - if (unlikely(q > xdr->end || q < p)) + if (unlikely(nwords > xdr->nwords || q > xdr->end || q < p)) return NULL; xdr->p = q; + xdr->nwords -= nwords; return p; } @@ -746,9 +751,16 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) struct xdr_buf *buf = xdr->buf; struct kvec *iov; ssize_t shift; + unsigned int nwords = XDR_QUADLEN(len); unsigned int end; int padding; + if (xdr->nwords == 0) + return; + if (nwords > xdr->nwords) { + nwords = xdr->nwords; + len = nwords << 2; + } /* Realign pages to current pointer position */ iov = buf->head; shift = iov->iov_len + (char *)iov->iov_base - (char *)xdr->p; @@ -758,15 +770,15 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) /* Truncate page data and move it into the tail */ if (buf->page_len > len) xdr_shrink_pagelen(buf, buf->page_len - len); - padding = (XDR_QUADLEN(len) << 2) - len; + padding = (nwords << 2) - len; xdr->iov = iov = buf->tail; /* Compute remaining message length. */ end = iov->iov_len; shift = buf->buflen - buf->len; - if (shift < end) + if (end > shift + padding) end -= shift; - else if (shift > 0) - end = 0; + else + end = padding; /* * Position current pointer at beginning of tail, and * set remaining message length. @@ -774,6 +786,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) xdr->p = (__be32 *)((char *)iov->iov_base + padding); xdr->end = (__be32 *)((char *)iov->iov_base + end); xdr->page_ptr = NULL; + xdr->nwords = XDR_QUADLEN(end - padding); } EXPORT_SYMBOL_GPL(xdr_read_pages); @@ -795,6 +808,7 @@ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) * set remaining message length. */ xdr_set_page_base(xdr, 0, len); + xdr->nwords += XDR_QUADLEN(xdr->buf->page_len); } EXPORT_SYMBOL_GPL(xdr_enter_page); -- cgit v1.2.3 From c337d3655ce85e12c7c476cb81406521926cacd2 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 21 Jun 2012 17:05:37 -0400 Subject: SUNRPC: xdr_read_pages should return the amount of XDR encoded page data Callers of xdr_read_pages() will want to know exactly how much XDR data is encoded in the pages after the data realignment. Signed-off-by: Trond Myklebust --- net/sunrpc/xdr.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'net/sunrpc/xdr.c') diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 5643feb6c645..80d518644cf8 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -745,8 +745,10 @@ EXPORT_SYMBOL_GPL(xdr_inline_decode); * Moves data beyond the current pointer position from the XDR head[] buffer * into the page list. Any data that lies beyond current position + "len" * bytes is moved into the XDR tail[]. + * + * Returns the number of XDR encoded bytes now contained in the pages */ -void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) +unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) { struct xdr_buf *buf = xdr->buf; struct kvec *iov; @@ -756,7 +758,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) int padding; if (xdr->nwords == 0) - return; + return 0; if (nwords > xdr->nwords) { nwords = xdr->nwords; len = nwords << 2; @@ -787,6 +789,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) xdr->end = (__be32 *)((char *)iov->iov_base + end); xdr->page_ptr = NULL; xdr->nwords = XDR_QUADLEN(end - padding); + return len; } EXPORT_SYMBOL_GPL(xdr_read_pages); @@ -802,7 +805,7 @@ EXPORT_SYMBOL_GPL(xdr_read_pages); */ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) { - xdr_read_pages(xdr, len); + len = xdr_read_pages(xdr, len); /* * Position current pointer at beginning of tail, and * set remaining message length. -- cgit v1.2.3 From 4517d526c8aa31b5c14165ef180cc19518ff0a35 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 21 Jun 2012 17:14:46 -0400 Subject: SUNRPC: Add the helper xdr_stream_pos Add a helper to report the current offset from the start of the xdr_stream. Signed-off-by: Trond Myklebust --- net/sunrpc/xdr.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'net/sunrpc/xdr.c') diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 80d518644cf8..95980a5cd0a8 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -454,6 +454,16 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len) } EXPORT_SYMBOL_GPL(xdr_shift_buf); +/** + * xdr_stream_pos - Return the current offset from the start of the xdr_stream + * @xdr: pointer to struct xdr_stream + */ +unsigned int xdr_stream_pos(const struct xdr_stream *xdr) +{ + return (unsigned int)(XDR_QUADLEN(xdr->buf->len) - xdr->nwords) << 2; +} +EXPORT_SYMBOL_GPL(xdr_stream_pos); + /** * xdr_init_encode - Initialize a struct xdr_stream for sending data. * @xdr: pointer to xdr_stream struct -- cgit v1.2.3 From b760b3131d962dd35925fb65956afe621fa65ec4 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 26 Jun 2012 12:19:55 -0400 Subject: SUNRPC: Remove open coded stream position calculation in xdr_read_pages Use xdr_stream_pos() instead. Signed-off-by: Trond Myklebust --- net/sunrpc/xdr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net/sunrpc/xdr.c') diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 95980a5cd0a8..faf6753c593d 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -764,6 +764,7 @@ unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) struct kvec *iov; ssize_t shift; unsigned int nwords = XDR_QUADLEN(len); + unsigned int cur = xdr_stream_pos(xdr); unsigned int end; int padding; @@ -775,9 +776,8 @@ unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) } /* Realign pages to current pointer position */ iov = buf->head; - shift = iov->iov_len + (char *)iov->iov_base - (char *)xdr->p; - if (shift > 0) - xdr_shrink_bufhead(buf, shift); + if (iov->iov_len > cur) + xdr_shrink_bufhead(buf, iov->iov_len - cur); /* Truncate page data and move it into the tail */ if (buf->page_len > len) -- cgit v1.2.3 From bd00f84bc57f42df32c728e86329a5c30f221657 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 26 Jun 2012 13:50:43 -0400 Subject: SUNRPC: Simplify the end-of-buffer calculation in xdr_read_pages Signed-off-by: Trond Myklebust --- net/sunrpc/xdr.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'net/sunrpc/xdr.c') diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index faf6753c593d..834d4da9cdb0 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -762,11 +762,10 @@ unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) { struct xdr_buf *buf = xdr->buf; struct kvec *iov; - ssize_t shift; unsigned int nwords = XDR_QUADLEN(len); unsigned int cur = xdr_stream_pos(xdr); unsigned int end; - int padding; + unsigned int padding; if (xdr->nwords == 0) return 0; @@ -782,15 +781,15 @@ unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) /* Truncate page data and move it into the tail */ if (buf->page_len > len) xdr_shrink_pagelen(buf, buf->page_len - len); + xdr->nwords = XDR_QUADLEN(buf->len - cur); + padding = (nwords << 2) - len; xdr->iov = iov = buf->tail; /* Compute remaining message length. */ - end = iov->iov_len; - shift = buf->buflen - buf->len; - if (end > shift + padding) - end -= shift; - else - end = padding; + end = ((xdr->nwords - nwords) << 2) + padding; + if (end > iov->iov_len) + end = iov->iov_len; + /* * Position current pointer at beginning of tail, and * set remaining message length. -- cgit v1.2.3 From 3994ee6fbf5185b3183f4585432226e786bfe86c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 26 Jun 2012 12:34:05 -0400 Subject: SUNRPC: Clean up xdr_read_pages Move the page alignment code into a separate helper. Signed-off-by: Trond Myklebust --- net/sunrpc/xdr.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'net/sunrpc/xdr.c') diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 834d4da9cdb0..03603f332fe6 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -747,25 +747,12 @@ __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) } EXPORT_SYMBOL_GPL(xdr_inline_decode); -/** - * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position - * @xdr: pointer to xdr_stream struct - * @len: number of bytes of page data - * - * Moves data beyond the current pointer position from the XDR head[] buffer - * into the page list. Any data that lies beyond current position + "len" - * bytes is moved into the XDR tail[]. - * - * Returns the number of XDR encoded bytes now contained in the pages - */ -unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) +static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len) { struct xdr_buf *buf = xdr->buf; struct kvec *iov; unsigned int nwords = XDR_QUADLEN(len); unsigned int cur = xdr_stream_pos(xdr); - unsigned int end; - unsigned int padding; if (xdr->nwords == 0) return 0; @@ -782,7 +769,32 @@ unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) if (buf->page_len > len) xdr_shrink_pagelen(buf, buf->page_len - len); xdr->nwords = XDR_QUADLEN(buf->len - cur); + return len; +} + +/** + * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position + * @xdr: pointer to xdr_stream struct + * @len: number of bytes of page data + * + * Moves data beyond the current pointer position from the XDR head[] buffer + * into the page list. Any data that lies beyond current position + "len" + * bytes is moved into the XDR tail[]. + * + * Returns the number of XDR encoded bytes now contained in the pages + */ +unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) +{ + struct xdr_buf *buf = xdr->buf; + struct kvec *iov; + unsigned int nwords; + unsigned int end; + unsigned int padding; + len = xdr_align_pages(xdr, len); + if (len == 0) + return 0; + nwords = XDR_QUADLEN(len); padding = (nwords << 2) - len; xdr->iov = iov = buf->tail; /* Compute remaining message length. */ -- cgit v1.2.3 From f8bb7f08549a1ced9ceb69a9bd5e163122044ab2 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 21 Jun 2012 14:53:10 -0400 Subject: SUNRPC: Clean up xdr_enter_page Use the xdr_align_pages() helper Signed-off-by: Trond Myklebust --- net/sunrpc/xdr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net/sunrpc/xdr.c') diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 03603f332fe6..2e3694eccd82 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -826,13 +826,13 @@ EXPORT_SYMBOL_GPL(xdr_read_pages); */ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) { - len = xdr_read_pages(xdr, len); + len = xdr_align_pages(xdr, len); /* * Position current pointer at beginning of tail, and * set remaining message length. */ - xdr_set_page_base(xdr, 0, len); - xdr->nwords += XDR_QUADLEN(xdr->buf->page_len); + if (len != 0) + xdr_set_page_base(xdr, 0, len); } EXPORT_SYMBOL_GPL(xdr_enter_page); -- cgit v1.2.3 From 140150dbb1f9cf3ef963fb55505f994d74ff3276 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 5 Jun 2012 15:20:25 -0400 Subject: SUNRPC: Remove unused function xdr_encode_pages Signed-off-by: Trond Myklebust --- net/sunrpc/xdr.c | 28 ---------------------------- 1 file changed, 28 deletions(-) (limited to 'net/sunrpc/xdr.c') diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 2e3694eccd82..d65d380571bc 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -128,34 +128,6 @@ xdr_terminate_string(struct xdr_buf *buf, const u32 len) } EXPORT_SYMBOL_GPL(xdr_terminate_string); -void -xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base, - unsigned int len) -{ - struct kvec *tail = xdr->tail; - u32 *p; - - xdr->pages = pages; - xdr->page_base = base; - xdr->page_len = len; - - p = (u32 *)xdr->head[0].iov_base + XDR_QUADLEN(xdr->head[0].iov_len); - tail->iov_base = p; - tail->iov_len = 0; - - if (len & 3) { - unsigned int pad = 4 - (len & 3); - - *p = 0; - tail->iov_base = (char *)p + (len & 3); - tail->iov_len = pad; - len += pad; - } - xdr->buflen += len; - xdr->len += len; -} -EXPORT_SYMBOL_GPL(xdr_encode_pages); - void xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, struct page **pages, unsigned int base, unsigned int len) -- cgit v1.2.3