summaryrefslogtreecommitdiffstats
path: root/fs/fuse
diff options
context:
space:
mode:
authorRobert Doebbelin <robert@quobyte.com>2016-03-07 09:50:56 +0100
committerBen Hutchings <ben@decadent.org.uk>2016-05-01 00:05:58 +0200
commit9277aac3687ed46aaf14b8a0593319cdb6db675b (patch)
tree425a4907c2ba4af75eeca3ff202f74c9278971ec /fs/fuse
parentdb88024c8130eb7380ac5d8e4a39bb3fd1ad6eb1 (diff)
downloadlinux-stable-9277aac3687ed46aaf14b8a0593319cdb6db675b.tar.gz
linux-stable-9277aac3687ed46aaf14b8a0593319cdb6db675b.tar.bz2
linux-stable-9277aac3687ed46aaf14b8a0593319cdb6db675b.zip
fuse: do not use iocb after it may have been freed
commit 7cabc61e01a0a8b663bd2b4c982aa53048218734 upstream. There's a race in fuse_direct_IO(), whereby is_sync_kiocb() is called on an iocb that could have been freed if async io has already completed. The fix in this case is simple and obvious: cache the result before starting io. It was discovered by KASan: kernel: ================================================================== kernel: BUG: KASan: use after free in fuse_direct_IO+0xb1a/0xcc0 at addr ffff88036c414390 Signed-off-by: Robert Doebbelin <robert@quobyte.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Fixes: bcba24ccdc82 ("fuse: enable asynchronous processing direct IO") [bwh: Backported to 3.16: adjust context] Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'fs/fuse')
-rw-r--r--fs/fuse/file.c5
1 files changed, 3 insertions, 2 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index e2a2c14a90ee..a6442041ab64 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -2876,6 +2876,7 @@ fuse_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter,
loff_t i_size;
size_t count = iov_iter_count(iter);
struct fuse_io_priv *io;
+ bool is_sync = is_sync_kiocb(iocb);
pos = offset;
inode = file->f_mapping->host;
@@ -2915,7 +2916,7 @@ fuse_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter,
* to wait on real async I/O requests, so we must submit this request
* synchronously.
*/
- if (!is_sync_kiocb(iocb) && (offset + count > i_size) && rw == WRITE)
+ if (!is_sync && (offset + count > i_size) && rw == WRITE)
io->async = false;
if (rw == WRITE)
@@ -2927,7 +2928,7 @@ fuse_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter,
fuse_aio_complete(io, ret < 0 ? ret : 0, -1);
/* we have a non-extending, async request, so return */
- if (!is_sync_kiocb(iocb))
+ if (!is_sync)
return -EIOCBQUEUED;
ret = wait_on_sync_kiocb(iocb);