]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
udf: Fix lock ordering in udf_evict_inode()
authorJan Kara <jack@suse.cz>
Mon, 20 May 2024 13:23:37 +0000 (15:23 +0200)
committerJan Kara <jack@suse.cz>
Wed, 5 Jun 2024 08:36:46 +0000 (10:36 +0200)
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: b9a861fd527a ("udf: Protect truncate and file type conversion with invalidate_lock")
Signed-off-by: Jan Kara <jack@suse.cz>
fs/udf/file.c
fs/udf/inode.c

index 97c59585208ca0903c9879c781bfe6c239b51d41..3a4179de316b4b6dbf11b17e3f2347cc7335b9b8 100644 (file)
@@ -232,7 +232,9 @@ static int udf_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
 
        if ((attr->ia_valid & ATTR_SIZE) &&
            attr->ia_size != i_size_read(inode)) {
+               filemap_invalidate_lock(inode->i_mapping);
                error = udf_setsize(inode, attr->ia_size);
+               filemap_invalidate_unlock(inode->i_mapping);
                if (error)
                        return error;
        }
index 9d39fdd26015349ad14e7eb93547abd777f15eb7..4726a4d014b60c65b1a75a0a99f15e588b888fbe 100644 (file)
@@ -1248,7 +1248,6 @@ int udf_setsize(struct inode *inode, loff_t newsize)
              S_ISLNK(inode->i_mode)))
                return -EINVAL;
 
-       filemap_invalidate_lock(inode->i_mapping);
        iinfo = UDF_I(inode);
        if (newsize > inode->i_size) {
                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);
                        if (err)
-                               goto out_unlock;
+                               return err;
                }
                err = udf_extend_file(inode, newsize);
                if (err)
-                       goto out_unlock;
+                       return err;
 set_size:
                truncate_setsize(inode, newsize);
        } else {
@@ -1283,14 +1282,14 @@ set_size:
                err = block_truncate_page(inode->i_mapping, newsize,
                                          udf_get_block);
                if (err)
-                       goto out_unlock;
+                       return err;
                truncate_setsize(inode, newsize);
                down_write(&iinfo->i_data_sem);
                udf_clear_extent_cache(inode);
                err = udf_truncate_extents(inode);
                up_write(&iinfo->i_data_sem);
                if (err)
-                       goto out_unlock;
+                       return err;
        }
 update_time:
        inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
@@ -1298,8 +1297,6 @@ update_time:
                udf_sync_inode(inode);
        else
                mark_inode_dirty(inode);
-out_unlock:
-       filemap_invalidate_unlock(inode->i_mapping);
        return err;
 }