aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Wilcox <willy@infradead.org>2019-04-05 17:02:10 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-04-14 13:00:04 -0400
commit15fab63e1e57be9fdb5eec1bbc5916e9825e9acb (patch)
tree1353971310401012cf4b108a375e525065d8bec0
parent8fde12ca79aff9b5ba951fce1a2641901b8d8e64 (diff)
fs: prevent page refcount overflow in pipe_buf_get
Change pipe_buf_get() to return a bool indicating whether it succeeded in raising the refcount of the page (if the thing in the pipe is a page). This removes another mechanism for overflowing the page refcount. All callers converted to handle a failure. Reported-by: Jann Horn <jannh@google.com> Signed-off-by: Matthew Wilcox <willy@infradead.org> Cc: stable@kernel.org Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/fuse/dev.c12
-rw-r--r--fs/pipe.c4
-rw-r--r--fs/splice.c12
-rw-r--r--include/linux/pipe_fs_i.h10
-rw-r--r--kernel/trace/trace.c6
5 files changed, 29 insertions, 15 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 809c0f2f9942..64f4de983468 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2034,10 +2034,8 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
2034 rem += pipe->bufs[(pipe->curbuf + idx) & (pipe->buffers - 1)].len; 2034 rem += pipe->bufs[(pipe->curbuf + idx) & (pipe->buffers - 1)].len;
2035 2035
2036 ret = -EINVAL; 2036 ret = -EINVAL;
2037 if (rem < len) { 2037 if (rem < len)
2038 pipe_unlock(pipe); 2038 goto out_free;
2039 goto out;
2040 }
2041 2039
2042 rem = len; 2040 rem = len;
2043 while (rem) { 2041 while (rem) {
@@ -2055,7 +2053,9 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
2055 pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1); 2053 pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1);
2056 pipe->nrbufs--; 2054 pipe->nrbufs--;
2057 } else { 2055 } else {
2058 pipe_buf_get(pipe, ibuf); 2056 if (!pipe_buf_get(pipe, ibuf))
2057 goto out_free;
2058
2059 *obuf = *ibuf; 2059 *obuf = *ibuf;
2060 obuf->flags &= ~PIPE_BUF_FLAG_GIFT; 2060 obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
2061 obuf->len = rem; 2061 obuf->len = rem;
@@ -2078,11 +2078,11 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
2078 ret = fuse_dev_do_write(fud, &cs, len); 2078 ret = fuse_dev_do_write(fud, &cs, len);
2079 2079
2080 pipe_lock(pipe); 2080 pipe_lock(pipe);
2081out_free:
2081 for (idx = 0; idx < nbuf; idx++) 2082 for (idx = 0; idx < nbuf; idx++)
2082 pipe_buf_release(pipe, &bufs[idx]); 2083 pipe_buf_release(pipe, &bufs[idx]);
2083 pipe_unlock(pipe); 2084 pipe_unlock(pipe);
2084 2085
2085out:
2086 kvfree(bufs); 2086 kvfree(bufs);
2087 return ret; 2087 return ret;
2088} 2088}
diff --git a/fs/pipe.c b/fs/pipe.c
index bdc5d3c0977d..b1543b85c14a 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -189,9 +189,9 @@ EXPORT_SYMBOL(generic_pipe_buf_steal);
189 * in the tee() system call, when we duplicate the buffers in one 189 * in the tee() system call, when we duplicate the buffers in one
190 * pipe into another. 190 * pipe into another.
191 */ 191 */
192void generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf) 192bool generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf)
193{ 193{
194 get_page(buf->page); 194 return try_get_page(buf->page);
195} 195}
196EXPORT_SYMBOL(generic_pipe_buf_get); 196EXPORT_SYMBOL(generic_pipe_buf_get);
197 197
diff --git a/fs/splice.c b/fs/splice.c
index de2ede048473..f30af82b850d 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -1588,7 +1588,11 @@ retry:
1588 * Get a reference to this pipe buffer, 1588 * Get a reference to this pipe buffer,
1589 * so we can copy the contents over. 1589 * so we can copy the contents over.
1590 */ 1590 */
1591 pipe_buf_get(ipipe, ibuf); 1591 if (!pipe_buf_get(ipipe, ibuf)) {
1592 if (ret == 0)
1593 ret = -EFAULT;
1594 break;
1595 }
1592 *obuf = *ibuf; 1596 *obuf = *ibuf;
1593 1597
1594 /* 1598 /*
@@ -1660,7 +1664,11 @@ static int link_pipe(struct pipe_inode_info *ipipe,
1660 * Get a reference to this pipe buffer, 1664 * Get a reference to this pipe buffer,
1661 * so we can copy the contents over. 1665 * so we can copy the contents over.
1662 */ 1666 */
1663 pipe_buf_get(ipipe, ibuf); 1667 if (!pipe_buf_get(ipipe, ibuf)) {
1668 if (ret == 0)
1669 ret = -EFAULT;
1670 break;
1671 }
1664 1672
1665 obuf = opipe->bufs + nbuf; 1673 obuf = opipe->bufs + nbuf;
1666 *obuf = *ibuf; 1674 *obuf = *ibuf;
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
index 5a3bb3b7c9ad..3f2a42c11e20 100644
--- a/include/linux/pipe_fs_i.h
+++ b/include/linux/pipe_fs_i.h
@@ -108,18 +108,20 @@ struct pipe_buf_operations {
108 /* 108 /*
109 * Get a reference to the pipe buffer. 109 * Get a reference to the pipe buffer.
110 */ 110 */
111 void (*get)(struct pipe_inode_info *, struct pipe_buffer *); 111 bool (*get)(struct pipe_inode_info *, struct pipe_buffer *);
112}; 112};
113 113
114/** 114/**
115 * pipe_buf_get - get a reference to a pipe_buffer 115 * pipe_buf_get - get a reference to a pipe_buffer
116 * @pipe: the pipe that the buffer belongs to 116 * @pipe: the pipe that the buffer belongs to
117 * @buf: the buffer to get a reference to 117 * @buf: the buffer to get a reference to
118 *
119 * Return: %true if the reference was successfully obtained.
118 */ 120 */
119static inline void pipe_buf_get(struct pipe_inode_info *pipe, 121static inline __must_check bool pipe_buf_get(struct pipe_inode_info *pipe,
120 struct pipe_buffer *buf) 122 struct pipe_buffer *buf)
121{ 123{
122 buf->ops->get(pipe, buf); 124 return buf->ops->get(pipe, buf);
123} 125}
124 126
125/** 127/**
@@ -178,7 +180,7 @@ struct pipe_inode_info *alloc_pipe_info(void);
178void free_pipe_info(struct pipe_inode_info *); 180void free_pipe_info(struct pipe_inode_info *);
179 181
180/* Generic pipe buffer ops functions */ 182/* Generic pipe buffer ops functions */
181void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); 183bool generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *);
182int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); 184int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *);
183int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); 185int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *);
184void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); 186void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *);
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index c4238b441624..0f300d488c9f 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -6835,12 +6835,16 @@ static void buffer_pipe_buf_release(struct pipe_inode_info *pipe,
6835 buf->private = 0; 6835 buf->private = 0;
6836} 6836}
6837 6837
6838static void buffer_pipe_buf_get(struct pipe_inode_info *pipe, 6838static bool buffer_pipe_buf_get(struct pipe_inode_info *pipe,
6839 struct pipe_buffer *buf) 6839 struct pipe_buffer *buf)
6840{ 6840{
6841 struct buffer_ref *ref = (struct buffer_ref *)buf->private; 6841 struct buffer_ref *ref = (struct buffer_ref *)buf->private;
6842 6842
6843 if (ref->ref > INT_MAX/2)
6844 return false;
6845
6843 ref->ref++; 6846 ref->ref++;
6847 return true;
6844} 6848}
6845 6849
6846/* Pipe buffer operations for a buffer. */ 6850/* Pipe buffer operations for a buffer. */