aboutsummaryrefslogtreecommitdiffstats
path: root/net/sunrpc
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2012-06-20 09:58:35 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2012-06-28 17:20:41 -0400
commitbfeea1dc1c9238f9e5a17a265489ecd0d19f66bb (patch)
tree009163b2f4c0255599d3c781a833c2dcfaa621ed /net/sunrpc
parent1537693ceaa8d6484f1ce631bec85658bfa9816c (diff)
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 <Trond.Myklebust@netapp.com>
Diffstat (limited to 'net/sunrpc')
-rw-r--r--net/sunrpc/xdr.c28
1 files changed, 21 insertions, 7 deletions
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)
630 xdr->buf = buf; 630 xdr->buf = buf;
631 xdr->scratch.iov_base = NULL; 631 xdr->scratch.iov_base = NULL;
632 xdr->scratch.iov_len = 0; 632 xdr->scratch.iov_len = 0;
633 xdr->nwords = XDR_QUADLEN(buf->len);
633 if (buf->head[0].iov_len != 0) 634 if (buf->head[0].iov_len != 0)
634 xdr_set_iov(xdr, buf->head, buf->len); 635 xdr_set_iov(xdr, buf->head, buf->len);
635 else if (buf->page_len != 0) 636 else if (buf->page_len != 0)
636 xdr_set_page_base(xdr, 0, buf->len); 637 xdr_set_page_base(xdr, 0, buf->len);
637 if (p != NULL && p > xdr->p && xdr->end >= p) 638 if (p != NULL && p > xdr->p && xdr->end >= p) {
639 xdr->nwords -= p - xdr->p;
638 xdr->p = p; 640 xdr->p = p;
641 }
639} 642}
640EXPORT_SYMBOL_GPL(xdr_init_decode); 643EXPORT_SYMBOL_GPL(xdr_init_decode);
641 644
@@ -660,12 +663,14 @@ EXPORT_SYMBOL_GPL(xdr_init_decode_pages);
660 663
661static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) 664static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
662{ 665{
666 unsigned int nwords = XDR_QUADLEN(nbytes);
663 __be32 *p = xdr->p; 667 __be32 *p = xdr->p;
664 __be32 *q = p + XDR_QUADLEN(nbytes); 668 __be32 *q = p + nwords;
665 669
666 if (unlikely(q > xdr->end || q < p)) 670 if (unlikely(nwords > xdr->nwords || q > xdr->end || q < p))
667 return NULL; 671 return NULL;
668 xdr->p = q; 672 xdr->p = q;
673 xdr->nwords -= nwords;
669 return p; 674 return p;
670} 675}
671 676
@@ -746,9 +751,16 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
746 struct xdr_buf *buf = xdr->buf; 751 struct xdr_buf *buf = xdr->buf;
747 struct kvec *iov; 752 struct kvec *iov;
748 ssize_t shift; 753 ssize_t shift;
754 unsigned int nwords = XDR_QUADLEN(len);
749 unsigned int end; 755 unsigned int end;
750 int padding; 756 int padding;
751 757
758 if (xdr->nwords == 0)
759 return;
760 if (nwords > xdr->nwords) {
761 nwords = xdr->nwords;
762 len = nwords << 2;
763 }
752 /* Realign pages to current pointer position */ 764 /* Realign pages to current pointer position */
753 iov = buf->head; 765 iov = buf->head;
754 shift = iov->iov_len + (char *)iov->iov_base - (char *)xdr->p; 766 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)
758 /* Truncate page data and move it into the tail */ 770 /* Truncate page data and move it into the tail */
759 if (buf->page_len > len) 771 if (buf->page_len > len)
760 xdr_shrink_pagelen(buf, buf->page_len - len); 772 xdr_shrink_pagelen(buf, buf->page_len - len);
761 padding = (XDR_QUADLEN(len) << 2) - len; 773 padding = (nwords << 2) - len;
762 xdr->iov = iov = buf->tail; 774 xdr->iov = iov = buf->tail;
763 /* Compute remaining message length. */ 775 /* Compute remaining message length. */
764 end = iov->iov_len; 776 end = iov->iov_len;
765 shift = buf->buflen - buf->len; 777 shift = buf->buflen - buf->len;
766 if (shift < end) 778 if (end > shift + padding)
767 end -= shift; 779 end -= shift;
768 else if (shift > 0) 780 else
769 end = 0; 781 end = padding;
770 /* 782 /*
771 * Position current pointer at beginning of tail, and 783 * Position current pointer at beginning of tail, and
772 * set remaining message length. 784 * set remaining message length.
@@ -774,6 +786,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
774 xdr->p = (__be32 *)((char *)iov->iov_base + padding); 786 xdr->p = (__be32 *)((char *)iov->iov_base + padding);
775 xdr->end = (__be32 *)((char *)iov->iov_base + end); 787 xdr->end = (__be32 *)((char *)iov->iov_base + end);
776 xdr->page_ptr = NULL; 788 xdr->page_ptr = NULL;
789 xdr->nwords = XDR_QUADLEN(end - padding);
777} 790}
778EXPORT_SYMBOL_GPL(xdr_read_pages); 791EXPORT_SYMBOL_GPL(xdr_read_pages);
779 792
@@ -795,6 +808,7 @@ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len)
795 * set remaining message length. 808 * set remaining message length.
796 */ 809 */
797 xdr_set_page_base(xdr, 0, len); 810 xdr_set_page_base(xdr, 0, len);
811 xdr->nwords += XDR_QUADLEN(xdr->buf->page_len);
798} 812}
799EXPORT_SYMBOL_GPL(xdr_enter_page); 813EXPORT_SYMBOL_GPL(xdr_enter_page);
800 814