Skip to content

Commit b9dc6f6

Browse files
author
Al Viro
committed
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: [email protected] # v4.9 Reported-by: "Alan J. Wylie" <[email protected]> Tested-by: "Alan J. Wylie" <[email protected]> Signed-off-by: Al Viro <[email protected]>
1 parent 4d22c75 commit b9dc6f6

File tree

1 file changed

+31
-23
lines changed

1 file changed

+31
-23
lines changed

lib/iov_iter.c

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -730,43 +730,50 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
730730
}
731731
EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
732732

733+
static 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+
733753
static void pipe_advance(struct iov_iter *i, size_t size)
734754
{
735755
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-
740756
if (unlikely(i->count < size))
741757
size = i->count;
742-
orig_sz = size;
743-
744758
if (size) {
759+
struct pipe_buffer *buf;
760+
size_t off = i->iov_offset, left = size;
761+
int idx = i->idx;
745762
if (off) /* make it relative to the beginning of buffer */
746-
size += off - pipe->bufs[idx].offset;
763+
left += off - pipe->bufs[idx].offset;
747764
while (1) {
748765
buf = &pipe->bufs[idx];
749-
if (size <= buf->len)
766+
if (left <= buf->len)
750767
break;
751-
size -= buf->len;
768+
left -= buf->len;
752769
idx = next_idx(idx, pipe);
753770
}
754-
buf->len = size;
755771
i->idx = idx;
756-
off = i->iov_offset = buf->offset + size;
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-
}
772+
i->iov_offset = buf->offset + left;
768773
}
769-
i->count -= orig_sz;
774+
i->count -= size;
775+
/* ... and discard everything past that point */
776+
pipe_truncate(i);
770777
}
771778

772779
void iov_iter_advance(struct iov_iter *i, size_t size)
@@ -826,6 +833,7 @@ void iov_iter_pipe(struct iov_iter *i, int direction,
826833
size_t count)
827834
{
828835
BUG_ON(direction != ITER_PIPE);
836+
WARN_ON(pipe->nrbufs == pipe->buffers);
829837
i->type = direction;
830838
i->pipe = pipe;
831839
i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);

0 commit comments

Comments
 (0)