summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-04-14 18:09:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-04-14 18:09:40 -0400
commit6b3a707736301c2128ca85ce85fb13f60b5e350a (patch)
tree2bf1892cf29121150adece8d1221ecd513a4e792 /fs
parent4443f8e6ac7755cd775c70d08be8042dc2f936cb (diff)
parent15fab63e1e57be9fdb5eec1bbc5916e9825e9acb (diff)
Merge branch 'page-refs' (page ref overflow)
Merge page ref overflow branch. Jann Horn reported that he can overflow the page ref count with sufficient memory (and a filesystem that is intentionally extremely slow). Admittedly it's not exactly easy. To have more than four billion references to a page requires a minimum of 32GB of kernel memory just for the pointers to the pages, much less any metadata to keep track of those pointers. Jann needed a total of 140GB of memory and a specially crafted filesystem that leaves all reads pending (in order to not ever free the page references and just keep adding more). Still, we have a fairly straightforward way to limit the two obvious user-controllable sources of page references: direct-IO like page references gotten through get_user_pages(), and the splice pipe page duplication. So let's just do that. * branch page-refs: fs: prevent page refcount overflow in pipe_buf_get mm: prevent get_user_pages() from overflowing page refcount mm: add 'try_get_page()' helper function mm: make page ref count overflow check tighter and more explicit
Diffstat (limited to 'fs')
-rw-r--r--fs/fuse/dev.c12
-rw-r--r--fs/pipe.c4
-rw-r--r--fs/splice.c12
3 files changed, 18 insertions, 10 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 8a63e52785e9..9971a35cf1ef 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2056,10 +2056,8 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
2056 rem += pipe->bufs[(pipe->curbuf + idx) & (pipe->buffers - 1)].len; 2056 rem += pipe->bufs[(pipe->curbuf + idx) & (pipe->buffers - 1)].len;
2057 2057
2058 ret = -EINVAL; 2058 ret = -EINVAL;
2059 if (rem < len) { 2059 if (rem < len)
2060 pipe_unlock(pipe); 2060 goto out_free;
2061 goto out;
2062 }
2063 2061
2064 rem = len; 2062 rem = len;
2065 while (rem) { 2063 while (rem) {
@@ -2077,7 +2075,9 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
2077 pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1); 2075 pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1);
2078 pipe->nrbufs--; 2076 pipe->nrbufs--;
2079 } else { 2077 } else {
2080 pipe_buf_get(pipe, ibuf); 2078 if (!pipe_buf_get(pipe, ibuf))
2079 goto out_free;
2080
2081 *obuf = *ibuf; 2081 *obuf = *ibuf;
2082 obuf->flags &= ~PIPE_BUF_FLAG_GIFT; 2082 obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
2083 obuf->len = rem; 2083 obuf->len = rem;
@@ -2100,11 +2100,11 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
2100 ret = fuse_dev_do_write(fud, &cs, len); 2100 ret = fuse_dev_do_write(fud, &cs, len);
2101 2101
2102 pipe_lock(pipe); 2102 pipe_lock(pipe);
2103out_free:
2103 for (idx = 0; idx < nbuf; idx++) 2104 for (idx = 0; idx < nbuf; idx++)
2104 pipe_buf_release(pipe, &bufs[idx]); 2105 pipe_buf_release(pipe, &bufs[idx]);
2105 pipe_unlock(pipe); 2106 pipe_unlock(pipe);
2106 2107
2107out:
2108 kvfree(bufs); 2108 kvfree(bufs);
2109 return ret; 2109 return ret;
2110} 2110}
diff --git a/fs/pipe.c b/fs/pipe.c
index 070aad543382..41065901106b 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -188,9 +188,9 @@ EXPORT_SYMBOL(generic_pipe_buf_steal);
188 * in the tee() system call, when we duplicate the buffers in one 188 * in the tee() system call, when we duplicate the buffers in one
189 * pipe into another. 189 * pipe into another.
190 */ 190 */
191void generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf) 191bool generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf)
192{ 192{
193 get_page(buf->page); 193 return try_get_page(buf->page);
194} 194}
195EXPORT_SYMBOL(generic_pipe_buf_get); 195EXPORT_SYMBOL(generic_pipe_buf_get);
196 196
diff --git a/fs/splice.c b/fs/splice.c
index 3ee7e82df48f..98943d9b219c 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -1593,7 +1593,11 @@ retry:
1593 * Get a reference to this pipe buffer, 1593 * Get a reference to this pipe buffer,
1594 * so we can copy the contents over. 1594 * so we can copy the contents over.
1595 */ 1595 */
1596 pipe_buf_get(ipipe, ibuf); 1596 if (!pipe_buf_get(ipipe, ibuf)) {
1597 if (ret == 0)
1598 ret = -EFAULT;
1599 break;
1600 }
1597 *obuf = *ibuf; 1601 *obuf = *ibuf;
1598 1602
1599 /* 1603 /*
@@ -1667,7 +1671,11 @@ static int link_pipe(struct pipe_inode_info *ipipe,
1667 * Get a reference to this pipe buffer, 1671 * Get a reference to this pipe buffer,
1668 * so we can copy the contents over. 1672 * so we can copy the contents over.
1669 */ 1673 */
1670 pipe_buf_get(ipipe, ibuf); 1674 if (!pipe_buf_get(ipipe, ibuf)) {
1675 if (ret == 0)
1676 ret = -EFAULT;
1677 break;
1678 }
1671 1679
1672 obuf = opipe->bufs + nbuf; 1680 obuf = opipe->bufs + nbuf;
1673 *obuf = *ibuf; 1681 *obuf = *ibuf;