summaryrefslogtreecommitdiffstats
path: root/fs/fuse
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/fuse
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/fuse')
-rw-r--r--fs/fuse/dev.c12
1 files changed, 6 insertions, 6 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}