diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-06-20 09:58:35 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-06-28 17:20:41 -0400 |
commit | bfeea1dc1c9238f9e5a17a265489ecd0d19f66bb (patch) | |
tree | 009163b2f4c0255599d3c781a833c2dcfaa621ed /net/sunrpc | |
parent | 1537693ceaa8d6484f1ce631bec85658bfa9816c (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.c | 28 |
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 | } |
640 | EXPORT_SYMBOL_GPL(xdr_init_decode); | 643 | EXPORT_SYMBOL_GPL(xdr_init_decode); |
641 | 644 | ||
@@ -660,12 +663,14 @@ EXPORT_SYMBOL_GPL(xdr_init_decode_pages); | |||
660 | 663 | ||
661 | static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) | 664 | static __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 | } |
778 | EXPORT_SYMBOL_GPL(xdr_read_pages); | 791 | EXPORT_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 | } |
799 | EXPORT_SYMBOL_GPL(xdr_enter_page); | 813 | EXPORT_SYMBOL_GPL(xdr_enter_page); |
800 | 814 | ||