Patch "pipe: iovec: Fix memory corruption when retrying atomic copy" has been added to the 3.10-stable tree
Subsystems (2)
This is a note to let you know that I've just added the patch titled
pipe: iovec: Fix memory corruption when retrying atomic copy
to the 3.10-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
pipe-iovec-fix-memory-corruption-when-retrying-atomi-3.14.patch
and it can be found in the queue-3.10 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
From: Ben Hutchings <[email protected]>
Date: Tue, 16 Jun 2015 22:11:06 +0100
Subject: pipe: iovec: Fix memory corruption when retrying atomic copy
as non-atomic
From: Ben Hutchings <[email protected]>
pipe_iov_copy_{from,to}_user() may be tried twice with the same iovec, the first time atomically and the second time not. The second attempt needs to continue from the iovec position, pipe buffer offset and remaining length where the first attempt failed, but currently the pipe buffer offset and remaining length are reset. This will corrupt the piped data (possibly also leading to an information leak between processes) and may also corrupt kernel memory.
This was fixed upstream by commits f0d1bec9d58d ("new helper:copy_page_from_iter()") and 637b58c2887e ("switch pipe_read() to copy_page_to_iter()"), but those aren't suitable for stable. This fix for older kernel versions was made by Seth Jennings for RHEL and I have extracted it from their update.
CVE-2015-1805
References: https://bugzilla.redhat.com/show_bug.cgi?id=1202855
--- fs/pipe.c | 55 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 23 deletions(-)
fs/pipe.c +32 -23
@@ -117,25 +117,27 @@ void pipe_wait(struct pipe_inode_info *p }static int -pipe_iov_copy_from_user (void *to,struct iovec *iov,unsigned long len, -int atomic) +pipe_iov_copy_from_user (void *addr,int *offset,struct iovec *iov, +size_t *remaining,int atomic) {unsigned long copy; -while (len >0 ) { +while (*remaining >0 ) {while (!iov->iov_len) iov++; - copy = min_t(unsigned long, len, iov->iov_len); + copy = min_t(unsigned long, *remaining, iov->iov_len);if (atomic) { -if (__copy_from_user_inatomic (to, iov->iov_base, copy)) +if (__copy_from_user_inatomic (addr + *offset, + iov->iov_base, copy))return -EFAULT ; }else { -if (copy_from_user (to, iov->iov_base, copy)) +if (copy_from_user (addr + *offset, + iov->iov_base, copy))return -EFAULT ; } - to += copy; - len -= copy; + *offset += copy; + *remaining -= copy; iov->iov_base += copy; iov->iov_len -= copy; }
@@ -143,25 +145,27 @@ pipe_iov_copy_from_user(void *to, struct }static int -pipe_iov_copy_to_user (struct iovec *iov,const void *from,unsigned long len, -int atomic) +pipe_iov_copy_to_user (struct iovec *iov,void *addr,int *offset, +size_t *remaining,int atomic) {unsigned long copy; -while (len >0 ) { +while (*remaining >0 ) {while (!iov->iov_len) iov++; - copy = min_t(unsigned long, len, iov->iov_len); + copy = min_t(unsigned long, *remaining, iov->iov_len);if (atomic) { -if (__copy_to_user_inatomic (iov->iov_base, from, copy)) +if (__copy_to_user_inatomic (iov->iov_base, + addr + *offset, copy))return -EFAULT ; }else { -if (copy_to_user (iov->iov_base, from, copy)) +if (copy_to_user (iov->iov_base, + addr + *offset, copy))return -EFAULT ; } - from += copy; - len -= copy; + *offset += copy; + *remaining -= copy; iov->iov_base += copy; iov->iov_len -= copy; }
@@ -395,7 +399,7 @@ pipe_read(struct kiocb *iocb, const strustruct pipe_buffer *buf = pipe->bufs + curbuf;const struct pipe_buf_operations *ops = buf->ops;void *addr; -size_t chars = buf->len; +size_t chars = buf->len, remaining;int error, atomic;if (chars > total_len)
@@ -409,9 +413,11 @@ pipe_read(struct kiocb *iocb, const stru } atomic = !iov_fault_in_pages_write (iov, chars); + remaining = chars; redo: addr = ops->map (pipe, buf, atomic); - error =pipe_iov_copy_to_user (iov, addr + buf->offset, chars, atomic); + error =pipe_iov_copy_to_user (iov, addr, &buf->offset, + &remaining, atomic); ops->unmap (pipe, buf, addr);if (unlikely (error)) {/*
@@ -426,7 +432,6 @@ redo:break ; } ret += chars; - buf->offset += chars; buf->len -= chars;/* Was it a packet buffer? Clean up and exit */
@@ -531,6 +536,7 @@ pipe_write(struct kiocb *iocb, const strif (ops->can_merge && offset + chars <=PAGE_SIZE ) {int error, atomic =1 ;void *addr; +size_t remaining = chars; error = ops->confirm (pipe, buf);if (error)
@@ -539,8 +545,8 @@ pipe_write(struct kiocb *iocb, const striov_fault_in_pages_read (iov, chars); redo1: addr = ops->map (pipe, buf, atomic); - error =pipe_iov_copy_from_user (offset + addr, iov, - chars, atomic); + error =pipe_iov_copy_from_user (addr, &offset, iov, + &remaining, atomic); ops->unmap (pipe, buf, addr); ret = error; do_wakeup =1 ;
@@ -575,6 +581,8 @@ redo1:struct page *page = pipe->tmp_page;char *src;int error, atomic =1 ; +int offset =0 ; +size_t remaining;if (!page) { page =alloc_page (GFP_HIGHUSER );
@@ -595,14 +603,15 @@ redo1: chars = total_len;iov_fault_in_pages_read (iov, chars); + remaining = chars; redo2:if (atomic) src =kmap_atomic (page);else src =kmap (page); - error =pipe_iov_copy_from_user (src, iov, chars, - atomic); + error =pipe_iov_copy_from_user (src, &offset, iov, + &remaining, atomic);if (atomic)kunmap_atomic (src);else
Patches currently in stable-queue which might be from [email protected] are
queue-3.10/pipe-iovec-fix-memory-corruption-when-retrying-atomi-3.14.patch