From: Miklos Szeredi mszeredi@redhat.com
mainline inclusion from mainline-v5.17-rc8 commit 0c4bcfdecb1ac0967619ee7ff44871d93c08c909 category: bugfix bugzilla: 186448, https://gitee.com/openeuler/kernel/issues/I4YS7O 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: Jann Horn jannh@google.com Fixes: c3021629a0d8 ("fuse: support splice() reading from fuse device") Cc: stable@vger.kernel.org Signed-off-by: Miklos Szeredi mszeredi@redhat.com Signed-off-by: Zhang Wensheng zhangwensheng5@huawei.com Reviewed-by: Hou Tao houtao1@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- fs/fuse/dev.c | 12 +++++++++++- fs/fuse/file.c | 1 + fs/fuse/fuse_i.h | 1 + 3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index d100b5dfedbd..8ac91ba05d6d 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -945,7 +945,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->args->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) { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index e63ce8443c96..a869c3a527a8 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1433,6 +1433,7 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii, (PAGE_SIZE - ret) & (PAGE_SIZE - 1); }
+ ap->args.user_pages = true; if (write) ap->args.in_pages = true; else diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index d31fc48c6afa..0686788e4283 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -266,6 +266,7 @@ struct fuse_args { bool nocreds:1; bool in_pages:1; bool out_pages:1; + bool user_pages:1; bool out_argvar:1; bool page_zeroing:1; bool page_replace:1;
From: Zhang Wensheng zhangwensheng5@huawei.com
mainline inclusion from mainline-v5.17-rc8 commit 0c4bcfdecb1ac0967619ee7ff44871d93c08c909 category: bugfix bugzilla: 186448, https://gitee.com/openeuler/kernel/issues/I4YS7O CVE: CVE-2022-1011
--------------------------------
Because create a new user_pages in fuse_args, to fix kabi change.
Signed-off-by: Zhang Wensheng zhangwensheng5@huawei.com Reviewed-by: Hou Tao houtao1@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- fs/fuse/fuse_i.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 0686788e4283..029f8e382c97 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -266,11 +266,13 @@ struct fuse_args { bool nocreds:1; bool in_pages:1; bool out_pages:1; - bool user_pages:1; bool out_argvar:1; bool page_zeroing:1; bool page_replace:1; bool may_block:1; +#ifndef __GENKSYMS__ + bool user_pages:1; +#endif struct fuse_in_arg in_args[3]; struct fuse_arg out_args[2]; void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);