Skip to content

Commit

Permalink
Merge tag 'for-6.12-tag' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/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
  • Loading branch information
torvalds committed Sep 23, 2024
2 parents d0359e4 + 7f1b63f commit a1fb2fc
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 4 deletions.
1 change: 1 addition & 0 deletions fs/btrfs/btrfs_inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions fs/btrfs/defrag.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
34 changes: 31 additions & 3 deletions fs/btrfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -3513,15 +3513,43 @@ 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.
* The private structure is used only for speeding up multiple
* 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)
Expand Down
2 changes: 1 addition & 1 deletion fs/btrfs/tree-checker.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit a1fb2fc

Please sign in to comment.