From patchwork Mon Mar 14 11:53:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg KH X-Patchwork-Id: 551343 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1D895C433EF for ; Mon, 14 Mar 2022 12:04:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240493AbiCNMFe (ORCPT ); Mon, 14 Mar 2022 08:05:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58736 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238216AbiCNMFM (ORCPT ); Mon, 14 Mar 2022 08:05:12 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 24BF848E6D; Mon, 14 Mar 2022 05:01:53 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id B158A61260; Mon, 14 Mar 2022 12:01:52 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id EC63EC340EC; Mon, 14 Mar 2022 12:01:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1647259312; bh=89SgiQbPp1G4g8GwaF17YM3Jfv4LmpvbyijXtDb/FsY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UqDHD9fqNZ7wO3J5Ajz0P0YG0Q6j4Bn6YVth6fkCSANsfIIk10Xm3hnBHkHYhBphM WsRiODSFVjYZR0MLN9mcEEGWGCb0YQ97yGf/FIwdUEukMzw3BOJscypBC535c4/7oQ uboVgHBNPIoMUulcxlFqgzV5Ne4WF84I/nwWFm7Q= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Jann Horn , Miklos Szeredi Subject: [PATCH 5.10 50/71] fuse: fix pipe buffer lifetime for direct_io Date: Mon, 14 Mar 2022 12:53:43 +0100 Message-Id: <20220314112739.331874028@linuxfoundation.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220314112737.929694832@linuxfoundation.org> References: <20220314112737.929694832@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: stable@vger.kernel.org From: Miklos Szeredi commit 0c4bcfdecb1ac0967619ee7ff44871d93c08c909 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 Fixes: c3021629a0d8 ("fuse: support splice() reading from fuse device") Cc: Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 12 +++++++++++- fs/fuse/file.c | 1 + fs/fuse/fuse_i.h | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -945,7 +945,17 @@ static int fuse_copy_page(struct fuse_co 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) { --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1418,6 +1418,7 @@ static int fuse_get_user_pages(struct fu (PAGE_SIZE - ret) & (PAGE_SIZE - 1); } + ap->args.user_pages = true; if (write) ap->args.in_pages = true; else --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -263,6 +263,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;