Commit a1fb2fcb authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-6.12-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux

Pull btrfs fixes from David Sterba:

 - fix dangling pointer to rb-tree of defragmented inodes after cleanup

 - a followup fix to handle concurrent lseek on the same fd that could
   leak memory under some conditions

 - fix wrong root id reported in tree checker when verifying dref

* tag 'for-6.12-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: fix use-after-free on rbtree that tracks inodes for auto defrag
  btrfs: tree-checker: fix the wrong output of data backref objectid
  btrfs: fix race setting file private on concurrent lseek using same fd
parents d0359e4c 7f1b63f9
......@@ -152,6 +152,7 @@ struct btrfs_inode {
* logged_trans), to access/update delalloc_bytes, new_delalloc_bytes,
* defrag_bytes, disk_i_size, outstanding_extents, csum_bytes and to
* update the VFS' inode number of bytes used.
* Also protects setting struct file::private_data.
*/
spinlock_t lock;
......
......@@ -463,6 +463,8 @@ struct btrfs_file_private {
void *filldir_buf;
u64 last_index;
struct extent_state *llseek_cached_state;
/* Task that allocated this structure. */
struct task_struct *owner_task;
};
static inline u32 BTRFS_LEAF_DATA_SIZE(const struct btrfs_fs_info *info)
......
......@@ -213,6 +213,8 @@ void btrfs_cleanup_defrag_inodes(struct btrfs_fs_info *fs_info)
&fs_info->defrag_inodes, rb_node)
kmem_cache_free(btrfs_inode_defrag_cachep, defrag);
fs_info->defrag_inodes = RB_ROOT;
spin_unlock(&fs_info->defrag_inodes_lock);
}
......
......@@ -3485,7 +3485,7 @@ static bool find_desired_extent_in_hole(struct btrfs_inode *inode, int whence,
static loff_t find_desired_extent(struct file *file, loff_t offset, int whence)
{
struct btrfs_inode *inode = BTRFS_I(file->f_mapping->host);
struct btrfs_file_private *private = file->private_data;
struct btrfs_file_private *private;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct extent_state *cached_state = NULL;
struct extent_state **delalloc_cached_state;
......@@ -3513,7 +3513,19 @@ static loff_t find_desired_extent(struct file *file, loff_t offset, int whence)
inode_get_bytes(&inode->vfs_inode) == i_size)
return i_size;
if (!private) {
spin_lock(&inode->lock);
private = file->private_data;
spin_unlock(&inode->lock);
if (private && private->owner_task != current) {
/*
* Not allocated by us, don't use it as its cached state is used
* by the task that allocated it and we don't want neither to
* mess with it nor get incorrect results because it reflects an
* invalid state for the current task.
*/
private = NULL;
} else if (!private) {
private = kzalloc(sizeof(*private), GFP_KERNEL);
/*
* No worries if memory allocation failed.
......@@ -3521,7 +3533,23 @@ static loff_t find_desired_extent(struct file *file, loff_t offset, int whence)
* lseek SEEK_HOLE/DATA calls to a file when there's delalloc,
* so everything will still be correct.
*/
file->private_data = private;
if (private) {
bool free = false;
private->owner_task = current;
spin_lock(&inode->lock);
if (file->private_data)
free = true;
else
file->private_data = private;
spin_unlock(&inode->lock);
if (free) {
kfree(private);
private = NULL;
}
}
}
if (private)
......
......@@ -1517,7 +1517,7 @@ static int check_extent_item(struct extent_buffer *leaf,
dref_objectid > BTRFS_LAST_FREE_OBJECTID)) {
extent_err(leaf, slot,
"invalid data ref objectid value %llu",
dref_root);
dref_objectid);
return -EUCLEAN;
}
if (unlikely(!IS_ALIGNED(dref_offset,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment