提交 ee8a1d43 编写于 作者: M Miklos Szeredi 提交者: Yongqiang Liu

fuse: fix pipe buffer lifetime for direct_io

mainline inclusion
from mainline-v5.17-rc8
commit 0c4bcfde
category: bugfix
bugzilla: 186448, https://gitee.com/openeuler/kernel/issues/I4YORE
CVE: CVE-2022-1011

--------------------------------

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: NJann Horn <jannh@google.com>
Fixes: c3021629 ("fuse: support splice() reading from fuse device")
Cc: <stable@vger.kernel.org>
Signed-off-by: NMiklos Szeredi <mszeredi@redhat.com>

Conflicts:
	fs/fuse/file.c
	fs/fuse/fuse_i.h
Signed-off-by: NZhang Wensheng <zhangwensheng5@huawei.com>
Reviewed-by: NTao Hou <houtao1@huawei.com>
Reviewed-by: NXiu Jianfeng <xiujianfeng@huawei.com>
Signed-off-by: NYongqiang Liu <liuyongqiang13@huawei.com>
上级 06ff79d5
......@@ -999,7 +999,17 @@ static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep,
while (count) {
if (cs->write && cs->pipebufs && page) {
return fuse_ref_page(cs, page, offset, count);
/*
* Can't control lifetime of pipe buffers, so always
* copy user pages.
*/
if (cs->req->in.user_pages) {
err = fuse_copy_fill(cs);
if (err)
return err;
} else {
return fuse_ref_page(cs, page, offset, count);
}
} else if (!cs->len) {
if (cs->move_pages && page &&
offset == 0 && count == PAGE_SIZE) {
......
......@@ -1321,10 +1321,12 @@ static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii,
(PAGE_SIZE - ret) & (PAGE_SIZE - 1);
}
if (write)
if (write) {
req->in.user_pages = 1;
req->in.argpages = 1;
else
} else {
req->out.argpages = 1;
}
*nbytesp = nbytes;
......
......@@ -174,6 +174,9 @@ struct fuse_in {
/** True if the data for the last argument is in req->pages */
unsigned argpages:1;
/** True if direct write */
unsigned user_pages:1;
/** Number of arguments */
unsigned numargs;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册