public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/2] udf: Fix race between file type conversion and writeback
@ 2026-03-26 14:06 Jan Kara
  2026-03-26 14:06 ` [PATCH v3 1/2] mpage: Provide variant of mpage_writepages() with own optional folio handler Jan Kara
  2026-03-26 14:06 ` [PATCH v3 2/2] udf: Fix race between file type conversion and writeback Jan Kara
  0 siblings, 2 replies; 5+ messages in thread
From: Jan Kara @ 2026-03-26 14:06 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christoph Hellwig, Jan Kara

Hello,

here is the third version of patches to fix a race between conversion of UDF
file from inline format to the standard format and writeback resulting in
possible memory corruption. As Christoph didn't like opencoding
folio_prepare_writeback() in UDF either, this version provides
mpage_writepages() variant with a per-folio callback instead. Review/Acks are
welcome in particular for the first patch. I'd like to push the fix to Linus
soon as it's kind of nasty issue.

Changes since v3:
* Create mpage_writepages() variant with a callback instead of opencoding.

Changes since v2:
* Opencoded folio_prepare_writeback() in udf instead of exporting it

Changes since v1:
* Fix crash in udf_writepages() when file had no folio in the mapping


								Honza
Previous versions:
Link: http://lore.kernel.org/r/20260323162617.2421-1-jack@suse.cz # v1
Link: http://lore.kernel.org/r/20260324105132.30490-1-jack@suse.cz # v2

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

* [PATCH v3 1/2] mpage: Provide variant of mpage_writepages() with own optional folio handler
  2026-03-26 14:06 [PATCH v4 0/2] udf: Fix race between file type conversion and writeback Jan Kara
@ 2026-03-26 14:06 ` Jan Kara
  2026-03-26 14:22   ` Christoph Hellwig
  2026-03-26 14:06 ` [PATCH v3 2/2] udf: Fix race between file type conversion and writeback Jan Kara
  1 sibling, 1 reply; 5+ messages in thread
From: Jan Kara @ 2026-03-26 14:06 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christoph Hellwig, Jan Kara

Some filesystems need to treat some folios specially (for example for
inodes with inline data). Doing the handling in their .writepages method
in a race-free manner results in duplicating some of the writeback
internals. So provide generalized version of mpage_writepages() that
allows filesystem to provide a handler called for each folio which can
handle the folio in a special way.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/mpage.c            | 21 +++++++++++++++++----
 include/linux/mpage.h | 11 +++++++++--
 2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/fs/mpage.c b/fs/mpage.c
index 7dae5afc2b9e..9095f3f983a8 100644
--- a/fs/mpage.c
+++ b/fs/mpage.c
@@ -655,8 +655,10 @@ static int mpage_write_folio(struct writeback_control *wbc, struct folio *folio,
  * address_space_operation.
  */
 int
-mpage_writepages(struct address_space *mapping,
-		struct writeback_control *wbc, get_block_t get_block)
+__mpage_writepages(struct address_space *mapping,
+		   struct writeback_control *wbc, get_block_t get_block,
+		   int (*write_folio)(struct folio *folio,
+				      struct writeback_control *wbc))
 {
 	struct mpage_data mpd = {
 		.get_block	= get_block,
@@ -666,11 +668,22 @@ mpage_writepages(struct address_space *mapping,
 	int error;
 
 	blk_start_plug(&plug);
-	while ((folio = writeback_iter(mapping, wbc, folio, &error)))
+	while ((folio = writeback_iter(mapping, wbc, folio, &error))) {
+		if (write_folio) {
+			error = write_folio(folio, wbc);
+			/*
+			 * == 0 means folio is handled, < 0 means error. In
+			 * both cases hand back control to writeback_iter()
+			 */
+			if (error <= 0)
+				continue;
+			/* Let mpage_write_folio() handle the folio. */
+		}
 		error = mpage_write_folio(wbc, folio, &mpd);
+	}
 	if (mpd.bio)
 		mpage_bio_submit_write(mpd.bio);
 	blk_finish_plug(&plug);
 	return error;
 }
-EXPORT_SYMBOL(mpage_writepages);
+EXPORT_SYMBOL(__mpage_writepages);
diff --git a/include/linux/mpage.h b/include/linux/mpage.h
index 1bdc39daac0a..358946990bfa 100644
--- a/include/linux/mpage.h
+++ b/include/linux/mpage.h
@@ -17,7 +17,14 @@ struct readahead_control;
 
 void mpage_readahead(struct readahead_control *, get_block_t get_block);
 int mpage_read_folio(struct folio *folio, get_block_t get_block);
-int mpage_writepages(struct address_space *mapping,
-		struct writeback_control *wbc, get_block_t get_block);
+int __mpage_writepages(struct address_space *mapping,
+		struct writeback_control *wbc, get_block_t get_block,
+		int (*write_folio)(struct folio *folio,
+				   struct writeback_control *wbc));
+static inline int mpage_writepages(struct address_space *mapping,
+		struct writeback_control *wbc, get_block_t get_block)
+{
+	return __mpage_writepages(mapping, wbc, get_block, NULL);
+}
 
 #endif
-- 
2.51.0


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

* [PATCH v3 2/2] udf: Fix race between file type conversion and writeback
  2026-03-26 14:06 [PATCH v4 0/2] udf: Fix race between file type conversion and writeback Jan Kara
  2026-03-26 14:06 ` [PATCH v3 1/2] mpage: Provide variant of mpage_writepages() with own optional folio handler Jan Kara
@ 2026-03-26 14:06 ` Jan Kara
  2026-03-26 14:22   ` Christoph Hellwig
  1 sibling, 1 reply; 5+ messages in thread
From: Jan Kara @ 2026-03-26 14:06 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christoph Hellwig, Jan Kara, Jianzhou Zhao

udf_setsize() can race with udf_writepages() as follows:

udf_setsize()			udf_writepages()
				  if (iinfo->i_alloc_type ==
						ICBTAG_FLAG_AD_IN_ICB)
  err = udf_expand_file_adinicb(inode);
  err = udf_extend_file(inode, newsize);
				    udf_adinicb_writepages()
				      memcpy_from_file_folio() - crash
					because inode size is too big.

Fix the problem by rechecking file type under folio lock in
udf_writepages() which properly serializes with
udf_expand_file_adinicb(). Since it is quite difficult to implement this
locking with current writeback_iter() logic, let's just opencode the
logic necessary to prepare (the only) folio the inode can have for
writeback.

Reported-by: Jianzhou Zhao <luckd0g@163.com>
Link: https://lore.kernel.org/all/f622c01.67ac.19cdbdd777d.Coremail.luckd0g@163.com
Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/udf/inode.c | 33 +++++++++++++++------------------
 1 file changed, 15 insertions(+), 18 deletions(-)

diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 7fae8002344a..23e894092dab 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -181,22 +181,23 @@ static void udf_write_failed(struct address_space *mapping, loff_t to)
 	}
 }
 
-static int udf_adinicb_writepages(struct address_space *mapping,
-		      struct writeback_control *wbc)
+static int udf_handle_page_wb(struct folio *folio,
+			      struct writeback_control *wbc)
 {
-	struct inode *inode = mapping->host;
+	struct inode *inode = folio->mapping->host;
 	struct udf_inode_info *iinfo = UDF_I(inode);
-	struct folio *folio = NULL;
-	int error = 0;
 
-	while ((folio = writeback_iter(mapping, wbc, folio, &error))) {
-		BUG_ON(!folio_test_locked(folio));
-		BUG_ON(folio->index != 0);
-		memcpy_from_file_folio(iinfo->i_data + iinfo->i_lenEAttr, folio,
-				0, i_size_read(inode));
-		folio_unlock(folio);
-	}
+	/*
+	 * Inodes in the normal format are handled by the generic code. This
+	 * check is race-free as the folio lock protects us from inode type
+	 * conversion.
+	 */
+	if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB)
+		return 1;
 
+	memcpy_from_file_folio(iinfo->i_data + iinfo->i_lenEAttr, folio,
+				0, i_size_read(inode));
+	folio_unlock(folio);
 	mark_inode_dirty(inode);
 	return 0;
 }
@@ -204,12 +205,8 @@ static int udf_adinicb_writepages(struct address_space *mapping,
 static int udf_writepages(struct address_space *mapping,
 			  struct writeback_control *wbc)
 {
-	struct inode *inode = mapping->host;
-	struct udf_inode_info *iinfo = UDF_I(inode);
-
-	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
-		return udf_adinicb_writepages(mapping, wbc);
-	return mpage_writepages(mapping, wbc, udf_get_block_wb);
+	return __mpage_writepages(mapping, wbc, udf_get_block_wb,
+				  udf_handle_page_wb);
 }
 
 static void udf_adinicb_read_folio(struct folio *folio)
-- 
2.51.0


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

* Re: [PATCH v3 1/2] mpage: Provide variant of mpage_writepages() with own optional folio handler
  2026-03-26 14:06 ` [PATCH v3 1/2] mpage: Provide variant of mpage_writepages() with own optional folio handler Jan Kara
@ 2026-03-26 14:22   ` Christoph Hellwig
  0 siblings, 0 replies; 5+ messages in thread
From: Christoph Hellwig @ 2026-03-26 14:22 UTC (permalink / raw)
  To: Jan Kara; +Cc: linux-fsdevel, Christoph Hellwig

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>


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

* Re: [PATCH v3 2/2] udf: Fix race between file type conversion and writeback
  2026-03-26 14:06 ` [PATCH v3 2/2] udf: Fix race between file type conversion and writeback Jan Kara
@ 2026-03-26 14:22   ` Christoph Hellwig
  0 siblings, 0 replies; 5+ messages in thread
From: Christoph Hellwig @ 2026-03-26 14:22 UTC (permalink / raw)
  To: Jan Kara; +Cc: linux-fsdevel, Christoph Hellwig, Jianzhou Zhao

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>


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

end of thread, other threads:[~2026-03-26 14:22 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-26 14:06 [PATCH v4 0/2] udf: Fix race between file type conversion and writeback Jan Kara
2026-03-26 14:06 ` [PATCH v3 1/2] mpage: Provide variant of mpage_writepages() with own optional folio handler Jan Kara
2026-03-26 14:22   ` Christoph Hellwig
2026-03-26 14:06 ` [PATCH v3 2/2] udf: Fix race between file type conversion and writeback Jan Kara
2026-03-26 14:22   ` Christoph Hellwig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox