mirror of
https://github.com/Divested-Mobile/DivestOS-Build.git
synced 2024-10-01 01:35:54 -04:00
109 lines
3.1 KiB
Diff
109 lines
3.1 KiB
Diff
From b9dc6f65bc5e232d1c05fe34b5daadc7e8bbf1fb Mon Sep 17 00:00:00 2001
|
|
From: Al Viro <viro@zeniv.linux.org.uk>
|
|
Date: Sat, 14 Jan 2017 19:33:08 -0500
|
|
Subject: 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>
|
|
---
|
|
lib/iov_iter.c | 54 +++++++++++++++++++++++++++++++-----------------------
|
|
1 file changed, 31 insertions(+), 23 deletions(-)
|
|
|
|
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
|
|
index 25f5723..e68604a 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,
|
|
}
|
|
EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
|
|
|
|
+static inline void pipe_truncate(struct iov_iter *i)
|
|
+{
|
|
+ struct pipe_inode_info *pipe = i->pipe;
|
|
+ if (pipe->nrbufs) {
|
|
+ size_t off = i->iov_offset;
|
|
+ int idx = i->idx;
|
|
+ int nrbufs = (idx - pipe->curbuf) & (pipe->buffers - 1);
|
|
+ if (off) {
|
|
+ pipe->bufs[idx].len = off - pipe->bufs[idx].offset;
|
|
+ idx = next_idx(idx, pipe);
|
|
+ nrbufs++;
|
|
+ }
|
|
+ while (pipe->nrbufs > nrbufs) {
|
|
+ pipe_buf_release(pipe, &pipe->bufs[idx]);
|
|
+ idx = next_idx(idx, pipe);
|
|
+ pipe->nrbufs--;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
static void pipe_advance(struct iov_iter *i, size_t size)
|
|
{
|
|
struct pipe_inode_info *pipe = i->pipe;
|
|
- struct pipe_buffer *buf;
|
|
- int idx = i->idx;
|
|
- size_t off = i->iov_offset, orig_sz;
|
|
-
|
|
if (unlikely(i->count < size))
|
|
size = i->count;
|
|
- orig_sz = size;
|
|
-
|
|
if (size) {
|
|
+ struct pipe_buffer *buf;
|
|
+ size_t off = i->iov_offset, left = size;
|
|
+ int idx = i->idx;
|
|
if (off) /* make it relative to the beginning of buffer */
|
|
- size += off - pipe->bufs[idx].offset;
|
|
+ left += off - pipe->bufs[idx].offset;
|
|
while (1) {
|
|
buf = &pipe->bufs[idx];
|
|
- if (size <= buf->len)
|
|
+ if (left <= buf->len)
|
|
break;
|
|
- size -= buf->len;
|
|
+ left -= buf->len;
|
|
idx = next_idx(idx, pipe);
|
|
}
|
|
- buf->len = size;
|
|
i->idx = idx;
|
|
- off = i->iov_offset = buf->offset + size;
|
|
- }
|
|
- if (off)
|
|
- idx = next_idx(idx, pipe);
|
|
- if (pipe->nrbufs) {
|
|
- int unused = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);
|
|
- /* [curbuf,unused) is in use. Free [idx,unused) */
|
|
- while (idx != unused) {
|
|
- pipe_buf_release(pipe, &pipe->bufs[idx]);
|
|
- idx = next_idx(idx, pipe);
|
|
- pipe->nrbufs--;
|
|
- }
|
|
+ i->iov_offset = buf->offset + left;
|
|
}
|
|
- i->count -= orig_sz;
|
|
+ i->count -= size;
|
|
+ /* ... and discard everything past that point */
|
|
+ pipe_truncate(i);
|
|
}
|
|
|
|
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,
|
|
size_t count)
|
|
{
|
|
BUG_ON(direction != ITER_PIPE);
|
|
+ WARN_ON(pipe->nrbufs == pipe->buffers);
|
|
i->type = direction;
|
|
i->pipe = pipe;
|
|
i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);
|
|
--
|
|
cgit v1.1
|
|
|