udf: Fix race between file type conversion and writeback
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 checking the file type under folio lock in udf_handle_page_wb() handler called from __mpage_writepages() which properly serializes with udf_expand_file_adinicb(). Reported-by: Jianzhou Zhao <luckd0g@163.com> Link: https://lore.kernel.org/all/f622c01.67ac.19cdbdd777d.Coremail.luckd0g@163.com Reviewed-by: Christoph Hellwig <hch@lst.de> Link: https://patch.msgid.link/20260326140635.15895-4-jack@suse.cz Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
parent
fffca572f9
commit
102e57d56f
|
|
@ -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,
|
static int udf_handle_page_wb(struct folio *folio,
|
||||||
struct writeback_control *wbc)
|
struct writeback_control *wbc)
|
||||||
{
|
{
|
||||||
struct inode *inode = mapping->host;
|
struct inode *inode = folio->mapping->host;
|
||||||
struct udf_inode_info *iinfo = UDF_I(inode);
|
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));
|
* Inodes in the normal format are handled by the generic code. This
|
||||||
BUG_ON(folio->index != 0);
|
* check is race-free as the folio lock protects us from inode type
|
||||||
memcpy_from_file_folio(iinfo->i_data + iinfo->i_lenEAttr, folio,
|
* 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));
|
0, i_size_read(inode));
|
||||||
folio_unlock(folio);
|
folio_unlock(folio);
|
||||||
}
|
|
||||||
|
|
||||||
mark_inode_dirty(inode);
|
mark_inode_dirty(inode);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -204,12 +205,8 @@ static int udf_adinicb_writepages(struct address_space *mapping,
|
||||||
static int udf_writepages(struct address_space *mapping,
|
static int udf_writepages(struct address_space *mapping,
|
||||||
struct writeback_control *wbc)
|
struct writeback_control *wbc)
|
||||||
{
|
{
|
||||||
struct inode *inode = mapping->host;
|
return __mpage_writepages(mapping, wbc, udf_get_block_wb,
|
||||||
struct udf_inode_info *iinfo = UDF_I(inode);
|
udf_handle_page_wb);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void udf_adinicb_read_folio(struct folio *folio)
|
static void udf_adinicb_read_folio(struct folio *folio)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue