Skip to content

Commit 49f9d93

Browse files
committed
fuse: fix pipe buffer lifetime for direct_io
jira VULN-7917 cve CVE-2022-1011 commit-author Miklos Szeredi <[email protected]> commit 0c4bcfd upstream-diff Used 4.19 LT commit 99db282 because page info is in fuse_req in this kernel as opposed to fuse_args in upstream 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]> (cherry picked from commit 0c4bcfd) Signed-off-by: Brett Mastbergen <[email protected]>
1 parent a57a126 commit 49f9d93

File tree

3 files changed

+14
-1
lines changed

3 files changed

+14
-1
lines changed

fs/fuse/dev.c

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

933933
while (count) {
934934
if (cs->write && cs->pipebufs && page) {
935-
return fuse_ref_page(cs, page, offset, count);
935+
/*
936+
* Can't control lifetime of pipe buffers, so always
937+
* copy user pages.
938+
*/
939+
if (cs->req->user_pages) {
940+
err = fuse_copy_fill(cs);
941+
if (err)
942+
return err;
943+
} else {
944+
return fuse_ref_page(cs, page, offset, count);
945+
}
936946
} else if (!cs->len) {
937947
if (cs->move_pages && page &&
938948
offset == 0 && count == PAGE_SIZE) {

fs/fuse/file.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,7 @@ static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii,
13861386
nbytes += frag_size;
13871387
}
13881388

1389+
req->user_pages = true;
13891390
if (write)
13901391
req->in.argpages = 1;
13911392
else

fs/fuse/fuse_i.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ struct fuse_req {
267267
/** refcount */
268268
atomic_t count;
269269

270+
bool user_pages;
271+
270272
/** Unique ID for the interrupt request */
271273
u64 intr_unique;
272274

0 commit comments

Comments
 (0)