History

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.

Al Viro committed 9 years ago

Commit b9dc6f61 parent 4d22c751 file · +31 −23Browse files

Unified · Split

lib/iov_iter.c+31−23
@@ -730,43 +730,50 @@ size_t iov_iter_copy_from_user_atomic(struct page *page, · 729 unmodified lines
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, · 53 unmodified lines
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);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t