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 Link: https://lore.kernel.org/all/f622c01.67ac.19cdbdd777d.Coremail.luckd0g@163.com Signed-off-by: Jan Kara --- fs/udf/inode.c | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 7fae8002344a..a659cd7d7ec0 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -181,34 +181,38 @@ 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_writepages(struct address_space *mapping, + struct writeback_control *wbc) { struct inode *inode = 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); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { + struct folio *folio; + + folio = filemap_lock_folio(mapping, 0); + if (IS_ERR(folio)) + return 0; + /* + * Recheck inode type under folio lock when we are protected + * against udf_expand_file_adinicb(). Bail to standard writeback + * path if file got expanded. + */ + if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { + folio_unlock(folio); + goto mpage_writeback; + } + if (folio_prepare_writeback(mapping, wbc, folio)) { + folio_unlock(folio); + return 0; + } 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; } - - mark_inode_dirty(inode); - return 0; -} - -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); +mpage_writeback: return mpage_writepages(mapping, wbc, udf_get_block_wb); } -- 2.51.0