Skip to content

Commit 0c4bcfd

Browse files
author
Miklos Szeredi
committed
fuse: fix pipe buffer lifetime for direct_io
In FOPEN_DIRECT_IO mode, fuse_file_write_iter() calls fuse_direct_write_iter(), which normally calls fuse_direct_io(), which then imports the write buffer with fuse_get_user_pages(), which uses iov_iter_get_pages() to grab references to userspace pages instead of actually copying memory. On the filesystem device side, these pages can then either be read to userspace (via fuse_dev_read()), or splice()d over into a pipe using fuse_dev_splice_read() as pipe buffers with &nosteal_pipe_buf_ops. This is wrong because after fuse_dev_do_read() unlocks the FUSE request, the userspace filesystem can mark the request as completed, causing write() to return. At that point, the userspace filesystem should no longer have access to the pipe buffer. Fix by copying pages coming from the user address space to new pipe buffers. Reported-by: Jann Horn <[email protected]> Fixes: c302162 ("fuse: support splice() reading from fuse device") Cc: <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent c086df4 commit 0c4bcfd

File tree

3 files changed

+13
-1
lines changed

3 files changed

+13
-1
lines changed

fs/fuse/dev.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -941,7 +941,17 @@ static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep,
941941

942942
while (count) {
943943
if (cs->write && cs->pipebufs && page) {
944-
return fuse_ref_page(cs, page, offset, count);
944+
/*
945+
* Can't control lifetime of pipe buffers, so always
946+
* copy user pages.
947+
*/
948+
if (cs->req->args->user_pages) {
949+
err = fuse_copy_fill(cs);
950+
if (err)
951+
return err;
952+
} else {
953+
return fuse_ref_page(cs, page, offset, count);
954+
}
945955
} else if (!cs->len) {
946956
if (cs->move_pages && page &&
947957
offset == 0 && count == PAGE_SIZE) {

fs/fuse/file.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,6 +1413,7 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii,
14131413
(PAGE_SIZE - ret) & (PAGE_SIZE - 1);
14141414
}
14151415

1416+
ap->args.user_pages = true;
14161417
if (write)
14171418
ap->args.in_pages = true;
14181419
else

fs/fuse/fuse_i.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ struct fuse_args {
256256
bool nocreds:1;
257257
bool in_pages:1;
258258
bool out_pages:1;
259+
bool user_pages:1;
259260
bool out_argvar:1;
260261
bool page_zeroing:1;
261262
bool page_replace:1;

0 commit comments

Comments
 (0)