summaryrefslogtreecommitdiffstats
path: root/lib/iov_iter.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2017-01-14 19:33:08 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2017-01-14 19:50:41 -0500
commitb9dc6f65bc5e232d1c05fe34b5daadc7e8bbf1fb (patch)
treeca221e796b274a6c909db003fcda215156aa4cc7 /lib/iov_iter.c
parent4d22c75d4c7b5c5f4bd31054f09103ee490878fd (diff)
fix a fencepost error in pipe_advance()
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. Cc: stable@vger.kernel.org # v4.9 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>
Diffstat (limited to 'lib/iov_iter.c')
-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 25f572303801..e68604ae3ced 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -730,43 +730,50 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
730} 730}
731EXPORT_SYMBOL(iov_iter_copy_from_user_atomic); 731EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
732 732
733static inline void pipe_truncate(struct iov_iter *i)
734{
735 struct pipe_inode_info *pipe = i->pipe;
736 if (pipe->nrbufs) {
737 size_t off = i->iov_offset;
738 int idx = i->idx;
739 int nrbufs = (idx - pipe->curbuf) & (pipe->buffers - 1);
740 if (off) {
741 pipe->bufs[idx].len = off - pipe->bufs[idx].offset;
742 idx = next_idx(idx, pipe);
743 nrbufs++;
744 }
745 while (pipe->nrbufs > nrbufs) {
746 pipe_buf_release(pipe, &pipe->bufs[idx]);
747 idx = next_idx(idx, pipe);
748 pipe->nrbufs--;
749 }
750 }
751}
752
733static void pipe_advance(struct iov_iter *i, size_t size) 753static void pipe_advance(struct iov_iter *i, size_t size)
734{ 754{
735 struct pipe_inode_info *pipe = i->pipe; 755 struct pipe_inode_info *pipe = i->pipe;
736 struct pipe_buffer *buf;
737 int idx = i->idx;
738 size_t off = i->iov_offset, orig_sz;
739
740 if (unlikely(i->count < size)) 756 if (unlikely(i->count < size))
741 size = i->count; 757 size = i->count;
742 orig_sz = size;
743
744 if (size) { 758 if (size) {
759 struct pipe_buffer *buf;
760 size_t off = i->iov_offset, left = size;
761 int idx = i->idx;
745 if (off) /* make it relative to the beginning of buffer */ 762 if (off) /* make it relative to the beginning of buffer */
746 size += off - pipe->bufs[idx].offset; 763 left += off - pipe->bufs[idx].offset;
747 while (1) { 764 while (1) {
748 buf = &pipe->bufs[idx]; 765 buf = &pipe->bufs[idx];
749 if (size <= buf->len) 766 if (left <= buf->len)
750 break; 767 break;
751 size -= buf->len; 768 left -= buf->len;
752 idx = next_idx(idx, pipe); 769 idx = next_idx(idx, pipe);
753 } 770 }
754 buf->len = size;
755 i->idx = idx; 771 i->idx = idx;
756 off = i->iov_offset = buf->offset + size; 772 i->iov_offset = buf->offset + left;
757 }
758 if (off)
759 idx = next_idx(idx, pipe);
760 if (pipe->nrbufs) {
761 int unused = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);
762 /* [curbuf,unused) is in use. Free [idx,unused) */
763 while (idx != unused) {
764 pipe_buf_release(pipe, &pipe->bufs[idx]);
765 idx = next_idx(idx, pipe);
766 pipe->nrbufs--;
767 }
768 } 773 }
769 i->count -= orig_sz; 774 i->count -= size;
775 /* ... and discard everything past that point */
776 pipe_truncate(i);
770} 777}
771 778
772void iov_iter_advance(struct iov_iter *i, size_t size) 779void iov_iter_advance(struct iov_iter *i, size_t size)
@@ -826,6 +833,7 @@ void iov_iter_pipe(struct iov_iter *i, int direction,
826 size_t count) 833 size_t count)
827{ 834{
828 BUG_ON(direction != ITER_PIPE); 835 BUG_ON(direction != ITER_PIPE);
836 WARN_ON(pipe->nrbufs == pipe->buffers);
829 i->type = direction; 837 i->type = direction;
830 i->pipe = pipe; 838 i->pipe = pipe;
831 i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1); 839 i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);