diff mbox series

[01/15] ubifs: Set page uptodate in the correct place

Message ID 20240120230824.2619716-2-willy@infradead.org
State Superseded
Headers show
Series ubifs folio conversion | expand

Commit Message

Matthew Wilcox Jan. 20, 2024, 11:08 p.m. UTC
Page cache reads are lockless, so setting the freshly allocated page
uptodate before we've overwritten it with the data it's supposed to have
in it will allow a simultaneous reader to see old data.  Move the call
to SetPageUptodate into ubifs_write_end(), which is after we copied the
new data into the page.

Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Cc: stable@vger.kernel.org
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
 fs/ubifs/file.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

Comments

Zhihao Cheng Jan. 22, 2024, 7:22 a.m. UTC | #1
在 2024/1/21 7:08, Matthew Wilcox (Oracle) 写道:
> Page cache reads are lockless, so setting the freshly allocated page
> uptodate before we've overwritten it with the data it's supposed to have
> in it will allow a simultaneous reader to see old data.  Move the call
> to SetPageUptodate into ubifs_write_end(), which is after we copied the
> new data into the page.
> 
> Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
> Cc: stable@vger.kernel.org
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> ---
>   fs/ubifs/file.c | 6 +++---
>   1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
> index 5029eb3390a5..40a9b03ef821 100644
> --- a/fs/ubifs/file.c
> +++ b/fs/ubifs/file.c
> @@ -463,9 +463,6 @@ static int ubifs_write_begin(struct file *file, struct address_space *mapping,
>   				return err;
>   			}
>   		}
> -
> -		SetPageUptodate(page);
> -		ClearPageError(page);
>   	}

This solution looks good to me, and I think 'SetPageUptodate' should be 
removed from write_begin_slow(slow path) too.

>   
>   	err = allocate_budget(c, page, ui, appending);
> @@ -569,6 +566,9 @@ static int ubifs_write_end(struct file *file, struct address_space *mapping,
>   		goto out;
>   	}
>   
> +	if (len == PAGE_SIZE)
> +		SetPageUptodate(page);
> +
>   	if (!PagePrivate(page)) {
>   		attach_page_private(page, (void *)1);
>   		atomic_long_inc(&c->dirty_pg_cnt);
>
Matthew Wilcox Jan. 22, 2024, 2:40 p.m. UTC | #2
On Mon, Jan 22, 2024 at 03:22:45PM +0800, Zhihao Cheng wrote:
> 在 2024/1/21 7:08, Matthew Wilcox (Oracle) 写道:
> > Page cache reads are lockless, so setting the freshly allocated page
> > uptodate before we've overwritten it with the data it's supposed to have
> > in it will allow a simultaneous reader to see old data.  Move the call
> > to SetPageUptodate into ubifs_write_end(), which is after we copied the
> > new data into the page.
> 
> This solution looks good to me, and I think 'SetPageUptodate' should be
> removed from write_begin_slow(slow path) too.

I didn't bother because we have just read into the page so it is
uptodate.  A racing read will see the data from before the write, but
that's an acceptable ordering of events.
Zhihao Cheng Jan. 23, 2024, 2:36 a.m. UTC | #3
在 2024/1/22 22:40, Matthew Wilcox 写道:
> On Mon, Jan 22, 2024 at 03:22:45PM +0800, Zhihao Cheng wrote:
>> 在 2024/1/21 7:08, Matthew Wilcox (Oracle) 写道:
>>> Page cache reads are lockless, so setting the freshly allocated page
>>> uptodate before we've overwritten it with the data it's supposed to have
>>> in it will allow a simultaneous reader to see old data.  Move the call
>>> to SetPageUptodate into ubifs_write_end(), which is after we copied the
>>> new data into the page.
>>
>> This solution looks good to me, and I think 'SetPageUptodate' should be
>> removed from write_begin_slow(slow path) too.
> 
> I didn't bother because we have just read into the page so it is
> uptodate.  A racing read will see the data from before the write, but
> that's an acceptable ordering of events.
> .
> 

I can't find where the page is read and set uptodate. I think the 
uninitialized data can be found in following path:

       writer               reader
ubifs_write_begin
  page1 = grab_cache_page_write_begin
  err = allocate_budget // ENOSPC
  unlock_page(page1)
  put_page(page1)
  write_begin_slow
   page2 = grab_cache_page_write_begin
   SetPageChecked(page2)
   SetPageUptodate(page2)
                 generic_file_read_iter
                  filemap_read
                   filemap_get_pages
                    filemap_get_read_batch
                    if (!folio_test_uptodate) // page2 is uptodate
                   copy_folio_to_iter // read uninitialized page content!
copy_page_from_iter_atomic // copy data to cover uninitialized page content
Matthew Wilcox Jan. 24, 2024, 5:01 a.m. UTC | #4
On Tue, Jan 23, 2024 at 10:36:14AM +0800, Zhihao Cheng wrote:
> 在 2024/1/22 22:40, Matthew Wilcox 写道:
> > On Mon, Jan 22, 2024 at 03:22:45PM +0800, Zhihao Cheng wrote:
> > > 在 2024/1/21 7:08, Matthew Wilcox (Oracle) 写道:
> > > > Page cache reads are lockless, so setting the freshly allocated page
> > > > uptodate before we've overwritten it with the data it's supposed to have
> > > > in it will allow a simultaneous reader to see old data.  Move the call
> > > > to SetPageUptodate into ubifs_write_end(), which is after we copied the
> > > > new data into the page.
> > > 
> > > This solution looks good to me, and I think 'SetPageUptodate' should be
> > > removed from write_begin_slow(slow path) too.
> > 
> > I didn't bother because we have just read into the page so it is
> > uptodate.  A racing read will see the data from before the write, but
> > that's an acceptable ordering of events.
> > .
> > 
> 
> I can't find where the page is read and set uptodate. I think the
> uninitialized data can be found in following path:

You're right; thanks.  I'd misread the code.  I'll send a new version in
a few hours.
diff mbox series

Patch

diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 5029eb3390a5..40a9b03ef821 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -463,9 +463,6 @@  static int ubifs_write_begin(struct file *file, struct address_space *mapping,
 				return err;
 			}
 		}
-
-		SetPageUptodate(page);
-		ClearPageError(page);
 	}
 
 	err = allocate_budget(c, page, ui, appending);
@@ -569,6 +566,9 @@  static int ubifs_write_end(struct file *file, struct address_space *mapping,
 		goto out;
 	}
 
+	if (len == PAGE_SIZE)
+		SetPageUptodate(page);
+
 	if (!PagePrivate(page)) {
 		attach_page_private(page, (void *)1);
 		atomic_long_inc(&c->dirty_pg_cnt);