All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] fuse: Don't make buffered read forward overflow value to a userspace process
@ 2020-02-03  7:36 Xiao Yang
  2020-02-05 14:37 ` Miklos Szeredi
  0 siblings, 1 reply; 6+ messages in thread
From: Xiao Yang @ 2020-02-03  7:36 UTC (permalink / raw)
  To: miklos, vgoyal, stefanha; +Cc: linux-fsdevel, yangx.jy, Xiao Yang

Buffered read in fuse normally goes via:
-> generic_file_buffered_read()
  ------------------------------
  -> fuse_readpages()
    -> fuse_send_readpages()
  or
  -> fuse_readpage() [if fuse_readpages() fails to get page]
    -> fuse_do_readpage()
  ------------------------------
      -> fuse_simple_request()

Buffered read changes original offset to page-aligned length by left-shift
and extends original count to be multiples of PAGE_SIZE and then fuse
forwards these new parameters to a userspace process, so it is possible
for the resulting offset(e.g page-aligned offset + extended count) to
exceed the whole file size(even the max value of off_t) when the userspace
process does read with new parameters.

xfstests generic/525 gets "pread: Invalid argument" error on virtiofs
because it triggers this issue.  See the following explanation:
PAGE_SIZE: 4096, file size: 2^63 - 1
Original: offset: 2^63 - 2, count: 1
Changed by buffered read: offset: 2^63 - 4096, count: 4096
New offset + new count exceeds the file size as well as LLONG_MAX

Make fuse calculate the number of bytes of data pages contain as
nfs_page_length() and generic_file_buffered_read() do, and then forward
page-aligned offset and normal count to a userspace process.

Signed-off-by: Xiao Yang <ice_yangxiao@163.com>
---
 fs/fuse/file.c | 28 ++++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index ce715380143c..5afc4b623eaf 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -19,6 +19,23 @@
 #include <linux/falloc.h>
 #include <linux/uio.h>
 
+static unsigned int fuse_page_length(struct page *page)
+{
+	loff_t i_size = i_size_read(page->mapping->host);
+
+	if (i_size > 0) {
+		pgoff_t index = page_index(page);
+		pgoff_t end_index = (i_size - 1) >> PAGE_SHIFT;
+
+		if (index < end_index)
+			return PAGE_SIZE;
+		if (index == end_index)
+			return ((i_size - 1) & ~PAGE_MASK) + 1;
+	}
+
+	return 0;
+}
+
 static struct page **fuse_pages_alloc(unsigned int npages, gfp_t flags,
 				      struct fuse_page_desc **desc)
 {
@@ -783,7 +800,7 @@ static int fuse_do_readpage(struct file *file, struct page *page)
 	struct inode *inode = page->mapping->host;
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	loff_t pos = page_offset(page);
-	struct fuse_page_desc desc = { .length = PAGE_SIZE };
+	struct fuse_page_desc desc = { .length = fuse_page_length(page) };
 	struct fuse_io_args ia = {
 		.ap.args.page_zeroing = true,
 		.ap.args.out_pages = true,
@@ -881,9 +898,12 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file)
 	struct fuse_conn *fc = ff->fc;
 	struct fuse_args_pages *ap = &ia->ap;
 	loff_t pos = page_offset(ap->pages[0]);
-	size_t count = ap->num_pages << PAGE_SHIFT;
+	size_t count = 0;
 	ssize_t res;
-	int err;
+	int err, i;
+
+	for (i = 0; i < ap->num_pages; i++)
+		count += ap->descs[i].length;
 
 	ap->args.out_pages = true;
 	ap->args.page_zeroing = true;
@@ -944,7 +964,7 @@ static int fuse_readpages_fill(void *_data, struct page *page)
 
 	get_page(page);
 	ap->pages[ap->num_pages] = page;
-	ap->descs[ap->num_pages].length = PAGE_SIZE;
+	ap->descs[ap->num_pages].length = fuse_page_length(page);
 	ap->num_pages++;
 	data->nr_pages--;
 	return 0;
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] fuse: Don't make buffered read forward overflow value to a userspace process
  2020-02-03  7:36 [PATCH] fuse: Don't make buffered read forward overflow value to a userspace process Xiao Yang
@ 2020-02-05 14:37 ` Miklos Szeredi
  2020-02-06 12:14   ` Miklos Szeredi
  0 siblings, 1 reply; 6+ messages in thread
From: Miklos Szeredi @ 2020-02-05 14:37 UTC (permalink / raw)
  To: Xiao Yang; +Cc: Vivek Goyal, Stefan Hajnoczi, linux-fsdevel, yangx.jy

On Mon, Feb 3, 2020 at 8:37 AM Xiao Yang <ice_yangxiao@163.com> wrote:
>
> Buffered read in fuse normally goes via:
> -> generic_file_buffered_read()
>   ------------------------------
>   -> fuse_readpages()
>     -> fuse_send_readpages()
>   or
>   -> fuse_readpage() [if fuse_readpages() fails to get page]
>     -> fuse_do_readpage()
>   ------------------------------
>       -> fuse_simple_request()
>
> Buffered read changes original offset to page-aligned length by left-shift
> and extends original count to be multiples of PAGE_SIZE and then fuse
> forwards these new parameters to a userspace process, so it is possible
> for the resulting offset(e.g page-aligned offset + extended count) to
> exceed the whole file size(even the max value of off_t) when the userspace
> process does read with new parameters.
>
> xfstests generic/525 gets "pread: Invalid argument" error on virtiofs
> because it triggers this issue.  See the following explanation:
> PAGE_SIZE: 4096, file size: 2^63 - 1
> Original: offset: 2^63 - 2, count: 1
> Changed by buffered read: offset: 2^63 - 4096, count: 4096
> New offset + new count exceeds the file size as well as LLONG_MAX

Thanks for the report and analysis.

However this patch may corrupt the cache if i_size changes between
calls to fuse_page_length().  (e.g. first page length set to 33;
second page length to 45; then 33-4095 will be zeroed and 4096-4140
will be filled from offset 33-77).  This will be mitigated by the
pages being invalidated when i_size changes
(fuse_change_attributes()).  Filling the pages with wrong data is not
a good idea regardless.

I think the best approach is first to just fix the xfstest reported
bug by clamping the end offset to LLONG_MAX.  That's a simple patch,
independent of i_size, and hence trivial to verify and hard to mess
up.

Then we can think about  clamping to i_size, whether it is necessary
and if it is, how to best implement it.

Thanks,
Miklos

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] fuse: Don't make buffered read forward overflow value to a userspace process
  2020-02-05 14:37 ` Miklos Szeredi
@ 2020-02-06 12:14   ` Miklos Szeredi
  2020-02-06 12:32     ` Xiao Yang
  0 siblings, 1 reply; 6+ messages in thread
From: Miklos Szeredi @ 2020-02-06 12:14 UTC (permalink / raw)
  To: Xiao Yang; +Cc: Vivek Goyal, Stefan Hajnoczi, linux-fsdevel, yangx.jy

On Wed, Feb 5, 2020 at 3:37 PM Miklos Szeredi <miklos@szeredi.hu> wrote:
>
> On Mon, Feb 3, 2020 at 8:37 AM Xiao Yang <ice_yangxiao@163.com> wrote:
> >
> > Buffered read in fuse normally goes via:
> > -> generic_file_buffered_read()
> >   ------------------------------
> >   -> fuse_readpages()
> >     -> fuse_send_readpages()
> >   or
> >   -> fuse_readpage() [if fuse_readpages() fails to get page]
> >     -> fuse_do_readpage()
> >   ------------------------------
> >       -> fuse_simple_request()
> >
> > Buffered read changes original offset to page-aligned length by left-shift
> > and extends original count to be multiples of PAGE_SIZE and then fuse
> > forwards these new parameters to a userspace process, so it is possible
> > for the resulting offset(e.g page-aligned offset + extended count) to
> > exceed the whole file size(even the max value of off_t) when the userspace
> > process does read with new parameters.
> >
> > xfstests generic/525 gets "pread: Invalid argument" error on virtiofs
> > because it triggers this issue.  See the following explanation:
> > PAGE_SIZE: 4096, file size: 2^63 - 1
> > Original: offset: 2^63 - 2, count: 1
> > Changed by buffered read: offset: 2^63 - 4096, count: 4096
> > New offset + new count exceeds the file size as well as LLONG_MAX
>
> Thanks for the report and analysis.
>
> However this patch may corrupt the cache if i_size changes between
> calls to fuse_page_length().  (e.g. first page length set to 33;
> second page length to 45; then 33-4095 will be zeroed and 4096-4140
> will be filled from offset 33-77).  This will be mitigated by the
> pages being invalidated when i_size changes
> (fuse_change_attributes()).  Filling the pages with wrong data is not
> a good idea regardless.
>
> I think the best approach is first to just fix the xfstest reported
> bug by clamping the end offset to LLONG_MAX.  That's a simple patch,
> independent of i_size, and hence trivial to verify and hard to mess
> up.

Applied a fix and pushed to:

  git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git#for-next

Thanks,
Miklos

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] fuse: Don't make buffered read forward overflow value to a userspace process
  2020-02-06 12:14   ` Miklos Szeredi
@ 2020-02-06 12:32     ` Xiao Yang
  2020-02-06 15:40       ` Miklos Szeredi
  0 siblings, 1 reply; 6+ messages in thread
From: Xiao Yang @ 2020-02-06 12:32 UTC (permalink / raw)
  To: Miklos Szeredi; +Cc: Vivek Goyal, Stefan Hajnoczi, linux-fsdevel, yangx.jy

On 2/6/20 8:14 PM, Miklos Szeredi wrote:
> On Wed, Feb 5, 2020 at 3:37 PM Miklos Szeredi <miklos@szeredi.hu> wrote:
>> On Mon, Feb 3, 2020 at 8:37 AM Xiao Yang <ice_yangxiao@163.com> wrote:
>>> Buffered read in fuse normally goes via:
>>> -> generic_file_buffered_read()
>>>    ------------------------------
>>>    -> fuse_readpages()
>>>      -> fuse_send_readpages()
>>>    or
>>>    -> fuse_readpage() [if fuse_readpages() fails to get page]
>>>      -> fuse_do_readpage()
>>>    ------------------------------
>>>        -> fuse_simple_request()
>>>
>>> Buffered read changes original offset to page-aligned length by left-shift
>>> and extends original count to be multiples of PAGE_SIZE and then fuse
>>> forwards these new parameters to a userspace process, so it is possible
>>> for the resulting offset(e.g page-aligned offset + extended count) to
>>> exceed the whole file size(even the max value of off_t) when the userspace
>>> process does read with new parameters.
>>>
>>> xfstests generic/525 gets "pread: Invalid argument" error on virtiofs
>>> because it triggers this issue.  See the following explanation:
>>> PAGE_SIZE: 4096, file size: 2^63 - 1
>>> Original: offset: 2^63 - 2, count: 1
>>> Changed by buffered read: offset: 2^63 - 4096, count: 4096
>>> New offset + new count exceeds the file size as well as LLONG_MAX
>> Thanks for the report and analysis.
>>
>> However this patch may corrupt the cache if i_size changes between
>> calls to fuse_page_length().  (e.g. first page length set to 33;
>> second page length to 45; then 33-4095 will be zeroed and 4096-4140
>> will be filled from offset 33-77).  This will be mitigated by the
>> pages being invalidated when i_size changes
>> (fuse_change_attributes()).  Filling the pages with wrong data is not
>> a good idea regardless.
>>
>> I think the best approach is first to just fix the xfstest reported
>> bug by clamping the end offset to LLONG_MAX.  That's a simple patch,
>> independent of i_size, and hence trivial to verify and hard to mess
>> up.
> Applied a fix and pushed to:
>
>    git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git#for-next

Hi Miklos,

Sorry for the late reply.

You have applied a fix quickly when I am going to send a patch today.

Just one comment for your fix:

I think we need to add the overflow check in fuse_send_readpages() and 
fuse_do_readpage().

Because generic_file_buffered_read() will call fuse_readpage() if 
fuse_readpages() fails to get page.

Thanks,

Xiao Yang

>
> Thanks,
> Miklos


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] fuse: Don't make buffered read forward overflow value to a userspace process
  2020-02-06 12:32     ` Xiao Yang
@ 2020-02-06 15:40       ` Miklos Szeredi
  2020-02-07  0:44         ` Xiao Yang
  0 siblings, 1 reply; 6+ messages in thread
From: Miklos Szeredi @ 2020-02-06 15:40 UTC (permalink / raw)
  To: Xiao Yang; +Cc: Vivek Goyal, Stefan Hajnoczi, linux-fsdevel, yangx.jy

On Thu, Feb 6, 2020 at 1:33 PM Xiao Yang <ice_yangxiao@163.com> wrote:
>
> On 2/6/20 8:14 PM, Miklos Szeredi wrote:
> > On Wed, Feb 5, 2020 at 3:37 PM Miklos Szeredi <miklos@szeredi.hu> wrote:
> >> On Mon, Feb 3, 2020 at 8:37 AM Xiao Yang <ice_yangxiao@163.com> wrote:
> >>> Buffered read in fuse normally goes via:
> >>> -> generic_file_buffered_read()
> >>>    ------------------------------
> >>>    -> fuse_readpages()
> >>>      -> fuse_send_readpages()
> >>>    or
> >>>    -> fuse_readpage() [if fuse_readpages() fails to get page]
> >>>      -> fuse_do_readpage()
> >>>    ------------------------------
> >>>        -> fuse_simple_request()
> >>>
> >>> Buffered read changes original offset to page-aligned length by left-shift
> >>> and extends original count to be multiples of PAGE_SIZE and then fuse
> >>> forwards these new parameters to a userspace process, so it is possible
> >>> for the resulting offset(e.g page-aligned offset + extended count) to
> >>> exceed the whole file size(even the max value of off_t) when the userspace
> >>> process does read with new parameters.
> >>>
> >>> xfstests generic/525 gets "pread: Invalid argument" error on virtiofs
> >>> because it triggers this issue.  See the following explanation:
> >>> PAGE_SIZE: 4096, file size: 2^63 - 1
> >>> Original: offset: 2^63 - 2, count: 1
> >>> Changed by buffered read: offset: 2^63 - 4096, count: 4096
> >>> New offset + new count exceeds the file size as well as LLONG_MAX
> >> Thanks for the report and analysis.
> >>
> >> However this patch may corrupt the cache if i_size changes between
> >> calls to fuse_page_length().  (e.g. first page length set to 33;
> >> second page length to 45; then 33-4095 will be zeroed and 4096-4140
> >> will be filled from offset 33-77).  This will be mitigated by the
> >> pages being invalidated when i_size changes
> >> (fuse_change_attributes()).  Filling the pages with wrong data is not
> >> a good idea regardless.
> >>
> >> I think the best approach is first to just fix the xfstest reported
> >> bug by clamping the end offset to LLONG_MAX.  That's a simple patch,
> >> independent of i_size, and hence trivial to verify and hard to mess
> >> up.
> > Applied a fix and pushed to:
> >
> >    git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git#for-next
>
> Hi Miklos,
>
> Sorry for the late reply.
>
> You have applied a fix quickly when I am going to send a patch today.
>
> Just one comment for your fix:
>
> I think we need to add the overflow check in fuse_send_readpages() and
> fuse_do_readpage().
>
> Because generic_file_buffered_read() will call fuse_readpage() if
> fuse_readpages() fails to get page.

Thanks, fixed.

Miklos

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] fuse: Don't make buffered read forward overflow value to a userspace process
  2020-02-06 15:40       ` Miklos Szeredi
@ 2020-02-07  0:44         ` Xiao Yang
  0 siblings, 0 replies; 6+ messages in thread
From: Xiao Yang @ 2020-02-07  0:44 UTC (permalink / raw)
  To: Miklos Szeredi; +Cc: Vivek Goyal, Stefan Hajnoczi, linux-fsdevel, yangx.jy


On 2/6/20 11:40 PM, Miklos Szeredi wrote:
> On Thu, Feb 6, 2020 at 1:33 PM Xiao Yang <ice_yangxiao@163.com> wrote:
>> On 2/6/20 8:14 PM, Miklos Szeredi wrote:
>>> On Wed, Feb 5, 2020 at 3:37 PM Miklos Szeredi <miklos@szeredi.hu> wrote:
>>>> On Mon, Feb 3, 2020 at 8:37 AM Xiao Yang <ice_yangxiao@163.com> wrote:
>>>>> Buffered read in fuse normally goes via:
>>>>> -> generic_file_buffered_read()
>>>>>     ------------------------------
>>>>>     -> fuse_readpages()
>>>>>       -> fuse_send_readpages()
>>>>>     or
>>>>>     -> fuse_readpage() [if fuse_readpages() fails to get page]
>>>>>       -> fuse_do_readpage()
>>>>>     ------------------------------
>>>>>         -> fuse_simple_request()
>>>>>
>>>>> Buffered read changes original offset to page-aligned length by left-shift
>>>>> and extends original count to be multiples of PAGE_SIZE and then fuse
>>>>> forwards these new parameters to a userspace process, so it is possible
>>>>> for the resulting offset(e.g page-aligned offset + extended count) to
>>>>> exceed the whole file size(even the max value of off_t) when the userspace
>>>>> process does read with new parameters.
>>>>>
>>>>> xfstests generic/525 gets "pread: Invalid argument" error on virtiofs
>>>>> because it triggers this issue.  See the following explanation:
>>>>> PAGE_SIZE: 4096, file size: 2^63 - 1
>>>>> Original: offset: 2^63 - 2, count: 1
>>>>> Changed by buffered read: offset: 2^63 - 4096, count: 4096
>>>>> New offset + new count exceeds the file size as well as LLONG_MAX
>>>> Thanks for the report and analysis.
>>>>
>>>> However this patch may corrupt the cache if i_size changes between
>>>> calls to fuse_page_length().  (e.g. first page length set to 33;
>>>> second page length to 45; then 33-4095 will be zeroed and 4096-4140
>>>> will be filled from offset 33-77).  This will be mitigated by the
>>>> pages being invalidated when i_size changes
>>>> (fuse_change_attributes()).  Filling the pages with wrong data is not
>>>> a good idea regardless.
>>>>
>>>> I think the best approach is first to just fix the xfstest reported
>>>> bug by clamping the end offset to LLONG_MAX.  That's a simple patch,
>>>> independent of i_size, and hence trivial to verify and hard to mess
>>>> up.
>>> Applied a fix and pushed to:
>>>
>>>     git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git#for-next
>> Hi Miklos,
>>
>> Sorry for the late reply.
>>
>> You have applied a fix quickly when I am going to send a patch today.
>>
>> Just one comment for your fix:
>>
>> I think we need to add the overflow check in fuse_send_readpages() and
>> fuse_do_readpage().
>>
>> Because generic_file_buffered_read() will call fuse_readpage() if
>> fuse_readpages() fails to get page.
> Thanks, fixed.

Hi Miklos,

Thanks a lot for your quick fix again. :-)

I have tested your patch and it works fine.

Reviewed-by: Xiao Yang <ice_yangxiao@163.com>

Thanks,

Xiao Yang

>
> Miklos


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-02-07  0:59 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-03  7:36 [PATCH] fuse: Don't make buffered read forward overflow value to a userspace process Xiao Yang
2020-02-05 14:37 ` Miklos Szeredi
2020-02-06 12:14   ` Miklos Szeredi
2020-02-06 12:32     ` Xiao Yang
2020-02-06 15:40       ` Miklos Szeredi
2020-02-07  0:44         ` Xiao Yang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.