aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2017-01-14 19:33:08 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-19 14:18:02 -0500
commitd06367ac1730ded79aa78307126236bf83af95a3 (patch)
tree451e2129d59ba675d7ef07ac628e38923d4f98b0
parentab8957396a692d46a357aec8ff57abc9bd5a878a (diff)
fix a fencepost error in pipe_advance()
commit b9dc6f65bc5e232d1c05fe34b5daadc7e8bbf1fb upstream. The logics in pipe_advance() used to release all buffers past the new position failed in cases when the number of buffers to release was equal to pipe->buffers. If that happened, none of them had been released, leaving pipe full. Worse, it was trivial to trigger and we end up with pipe full of uninitialized pages. IOW, it's an infoleak. Reported-by: "Alan J. Wylie" <alan@wylie.me.uk> Tested-by: "Alan J. Wylie" <alan@wylie.me.uk> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--lib/iov_iter.c54
1 files changed, 31 insertions, 23 deletions
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index f2bd21b93dfc..efb0b4d267a1 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -678,43 +678,50 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
678} 678}
679EXPORT_SYMBOL(iov_iter_copy_from_user_atomic); 679EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
680 680
681static inline void pipe_truncate(struct iov_iter *i)
682{
683 struct pipe_inode_info *pipe = i->pipe;
684 if (pipe->nrbufs) {
685 size_t off = i->iov_offset;
686 int idx = i->idx;
687 int nrbufs = (idx - pipe->curbuf) & (pipe->buffers - 1);
688 if (off) {
689 pipe->bufs[idx].len = off - pipe->bufs[idx].offset;
690 idx = next_idx(idx, pipe);
691 nrbufs++;
692 }
693 while (pipe->nrbufs > nrbufs) {
694 pipe_buf_release(pipe, &pipe->bufs[idx]);
695 idx = next_idx(idx, pipe);
696 pipe->nrbufs--;
697 }
698 }
699}
700
681static void pipe_advance(struct iov_iter *i, size_t size) 701static void pipe_advance(struct iov_iter *i, size_t size)
682{ 702{
683 struct pipe_inode_info *pipe = i->pipe; 703 struct pipe_inode_info *pipe = i->pipe;
684 struct pipe_buffer *buf;
685 int idx = i->idx;
686 size_t off = i->iov_offset, orig_sz;
687
688 if (unlikely(i->count < size)) 704 if (unlikely(i->count < size))
689 size = i->count; 705 size = i->count;
690 orig_sz = size;
691
692 if (size) { 706 if (size) {
707 struct pipe_buffer *buf;
708 size_t off = i->iov_offset, left = size;
709 int idx = i->idx;
693 if (off) /* make it relative to the beginning of buffer */ 710 if (off) /* make it relative to the beginning of buffer */
694 size += off - pipe->bufs[idx].offset; 711 left += off - pipe->bufs[idx].offset;
695 while (1) { 712 while (1) {
696 buf = &pipe->bufs[idx]; 713 buf = &pipe->bufs[idx];
697 if (size <= buf->len) 714 if (left <= buf->len)
698 break; 715 break;
699 size -= buf->len; 716 left -= buf->len;
700 idx = next_idx(idx, pipe); 717 idx = next_idx(idx, pipe);
701 } 718 }
702 buf->len = size;
703 i->idx = idx; 719 i->idx = idx;
704 off = i->iov_offset = buf->offset + size; 720 i->iov_offset = buf->offset + left;
705 }
706 if (off)
707 idx = next_idx(idx, pipe);
708 if (pipe->nrbufs) {
709 int unused = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);
710 /* [curbuf,unused) is in use. Free [idx,unused) */
711 while (idx != unused) {
712 pipe_buf_release(pipe, &pipe->bufs[idx]);
713 idx = next_idx(idx, pipe);
714 pipe->nrbufs--;
715 }
716 } 721 }
717 i->count -= orig_sz; 722 i->count -= size;
723 /* ... and discard everything past that point */
724 pipe_truncate(i);
718} 725}
719 726
720void iov_iter_advance(struct iov_iter *i, size_t size) 727void iov_iter_advance(struct iov_iter *i, size_t size)
@@ -774,6 +781,7 @@ void iov_iter_pipe(struct iov_iter *i, int direction,
774 size_t count) 781 size_t count)
775{ 782{
776 BUG_ON(direction != ITER_PIPE); 783 BUG_ON(direction != ITER_PIPE);
784 WARN_ON(pipe->nrbufs == pipe->buffers);
777 i->type = direction; 785 i->type = direction;
778 i->pipe = pipe; 786 i->pipe = pipe;
779 i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1); 787 i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);