udf: Fix lock ordering in udf_evict_inode()
udf_evict_inode() calls udf_setsize() to truncate deleted inode.
However inode deletion through udf_evict_inode() can happen from inode
reclaim context and udf_setsize() grabs mapping->invalidate_lock which
isn't generally safe to acquire from fs reclaim context since we
allocate pages under mapping->invalidate_lock for example in a page
fault path. This is however not a real deadlock possibility as by the
time udf_evict_inode() is called, nobody can be accessing the inode,
even less work with its page cache. So this is just a lockdep triggering
false positive. Fix the problem by moving mapping->invalidate_lock
locking outsize of udf_setsize() into udf_setattr() as grabbing
mapping->invalidate_lock from udf_evict_inode() is pointless.
Reported-by: syzbot+0333a6f4b88bcd68a62f@syzkaller.appspotmail.com
Fixes: b9a861fd52
("udf: Protect truncate and file type conversion with invalidate_lock")
Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
parent
c1f1b25a60
commit
8832fc1e50
@ -232,7 +232,9 @@ static int udf_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
|||||||
|
|
||||||
if ((attr->ia_valid & ATTR_SIZE) &&
|
if ((attr->ia_valid & ATTR_SIZE) &&
|
||||||
attr->ia_size != i_size_read(inode)) {
|
attr->ia_size != i_size_read(inode)) {
|
||||||
|
filemap_invalidate_lock(inode->i_mapping);
|
||||||
error = udf_setsize(inode, attr->ia_size);
|
error = udf_setsize(inode, attr->ia_size);
|
||||||
|
filemap_invalidate_unlock(inode->i_mapping);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -1248,7 +1248,6 @@ int udf_setsize(struct inode *inode, loff_t newsize)
|
|||||||
S_ISLNK(inode->i_mode)))
|
S_ISLNK(inode->i_mode)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
filemap_invalidate_lock(inode->i_mapping);
|
|
||||||
iinfo = UDF_I(inode);
|
iinfo = UDF_I(inode);
|
||||||
if (newsize > inode->i_size) {
|
if (newsize > inode->i_size) {
|
||||||
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
|
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
|
||||||
@ -1261,11 +1260,11 @@ int udf_setsize(struct inode *inode, loff_t newsize)
|
|||||||
}
|
}
|
||||||
err = udf_expand_file_adinicb(inode);
|
err = udf_expand_file_adinicb(inode);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_unlock;
|
return err;
|
||||||
}
|
}
|
||||||
err = udf_extend_file(inode, newsize);
|
err = udf_extend_file(inode, newsize);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_unlock;
|
return err;
|
||||||
set_size:
|
set_size:
|
||||||
truncate_setsize(inode, newsize);
|
truncate_setsize(inode, newsize);
|
||||||
} else {
|
} else {
|
||||||
@ -1283,14 +1282,14 @@ set_size:
|
|||||||
err = block_truncate_page(inode->i_mapping, newsize,
|
err = block_truncate_page(inode->i_mapping, newsize,
|
||||||
udf_get_block);
|
udf_get_block);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_unlock;
|
return err;
|
||||||
truncate_setsize(inode, newsize);
|
truncate_setsize(inode, newsize);
|
||||||
down_write(&iinfo->i_data_sem);
|
down_write(&iinfo->i_data_sem);
|
||||||
udf_clear_extent_cache(inode);
|
udf_clear_extent_cache(inode);
|
||||||
err = udf_truncate_extents(inode);
|
err = udf_truncate_extents(inode);
|
||||||
up_write(&iinfo->i_data_sem);
|
up_write(&iinfo->i_data_sem);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_unlock;
|
return err;
|
||||||
}
|
}
|
||||||
update_time:
|
update_time:
|
||||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||||
@ -1298,8 +1297,6 @@ update_time:
|
|||||||
udf_sync_inode(inode);
|
udf_sync_inode(inode);
|
||||||
else
|
else
|
||||||
mark_inode_dirty(inode);
|
mark_inode_dirty(inode);
|
||||||
out_unlock:
|
|
||||||
filemap_invalidate_unlock(inode->i_mapping);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user