FAILED: patch "[PATCH] f2fs: atomic: fix UAF issue on f2fs_inode_info.atomic_inode" failed to apply to 6.1-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.1-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.1.y
git checkout FETCH_HEAD
git cherry-pick -x e0288584baa5dc41df4a829a023c4c1b33fe53d7
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026070243-overload-marina-0abd@gregkh' --subject-prefix 'PATCH 6.1.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From e0288584baa5dc41df4a829a023c4c1b33fe53d7 Mon Sep 17 00:00:00 2001
From: Chao Yu <[email protected]>
Date: Thu, 21 May 2026 10:15:05 +0800
Subject: [PATCH] f2fs: atomic: fix UAF issue on f2fs_inode_info.atomic_inode
- ioctl(F2FS_IOC_GARBAGE_COLLECT_RANGE) - shrink
- f2fs_gc
- gc_data_segment
- ra_data_block(cow_inode)
- mapping = F2FS_I(inode)->atomic_inode->i_mapping
: f2fs_is_cow_file(cow_inode) is true
- f2fs_evict_inode(atomic_inode)
- clear_inode_flag(fi->cow_inode, FI_COW_FILE)
- F2FS_I(fi->cow_inode)->atomic_inode = NULL
...
- truncate_inode_pages_final(atomic_inode)
- f2fs_grab_cache_folio(mapping)
: create folio in atomic_inode->mapping
- clear_inode(atomic_inode)
- BUG_ON(atomic_inode->i_data.nrpages)
We need to add a reference on fi->atomic_inode before using its mapping field during garbage collection, otherwise, it will cause UAF issue.
fs/f2fs/gc.c +42 -8
@@ -1220,8 +1220,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,static int ra_data_block (struct inode *inode,pgoff_t index) {struct f2fs_sb_info *sbi =F2FS_I_SB (inode); -struct address_space *mapping =f2fs_is_cow_file (inode) ? -F2FS_I (inode)->atomic_inode->i_mapping : inode->i_mapping; +struct address_space *mapping = inode->i_mapping; +struct inode *atomic_inode =NULL ;struct dnode_of_data dn;struct folio *folio, *efolio;struct f2fs_io_info fio = {
@@ -1236,9 +1236,22 @@ static int ra_data_block(struct inode *inode, pgoff_t index) };int err =0 ; +f2fs_down_read (&F2FS_I (inode)->i_sem); +if (f2fs_is_cow_file (inode)) { + atomic_inode =igrab (F2FS_I (inode)->atomic_inode); +if (!atomic_inode) { +f2fs_up_read (&F2FS_I (inode)->i_sem); +return -EBUSY ; + } + mapping = atomic_inode->i_mapping; + } +f2fs_up_read (&F2FS_I (inode)->i_sem); + folio =f2fs_grab_cache_folio (mapping, index, true); -if (IS_ERR (folio)) -return PTR_ERR (folio); +if (IS_ERR (folio)) { + err =PTR_ERR (folio); + goto out_iput; + }if (f2fs_lookup_read_extent_cache_block (inode, index, &dn.data_blkaddr)) {
@@ -1299,11 +1312,16 @@ static int ra_data_block(struct inode *inode, pgoff_t index)f2fs_update_iostat (sbi, inode,FS_DATA_READ_IO ,F2FS_BLKSIZE );f2fs_update_iostat (sbi,NULL ,FS_GDATA_READ_IO ,F2FS_BLKSIZE ); +if (atomic_inode) +iput (atomic_inode);return 0 ; put_encrypted_page:f2fs_put_page (fio.encrypted_page, true); put_folio:f2fs_folio_put (folio, true); +out_iput: +if (atomic_inode) +iput (atomic_inode);return err; }
@@ -1314,8 +1332,8 @@ static int ra_data_block(struct inode *inode, pgoff_t index)static int move_data_block (struct inode *inode,block_t bidx,int gc_type,unsigned int segno,int off) { -struct address_space *mapping =f2fs_is_cow_file (inode) ? -F2FS_I (inode)->atomic_inode->i_mapping : inode->i_mapping; +struct address_space *mapping = inode->i_mapping; +struct inode *atomic_inode =NULL ;struct f2fs_io_info fio = { .sbi =F2FS_I_SB (inode), .ino = inode->i_ino,
@@ -1337,10 +1355,23 @@ static int move_data_block(struct inode *inode, block_t bidx, (fio.sbi->gc_mode !=GC_URGENT_HIGH ) ?CURSEG_ALL_DATA_ATGC :CURSEG_COLD_DATA ; +f2fs_down_read (&F2FS_I (inode)->i_sem); +if (f2fs_is_cow_file (inode)) { + atomic_inode =igrab (F2FS_I (inode)->atomic_inode); +if (!atomic_inode) { +f2fs_up_read (&F2FS_I (inode)->i_sem); +return -EBUSY ; + } + mapping = atomic_inode->i_mapping; + } +f2fs_up_read (&F2FS_I (inode)->i_sem); +/* do not read out */ folio =f2fs_grab_cache_folio (mapping, bidx, false); -if (IS_ERR (folio)) -return PTR_ERR (folio); +if (IS_ERR (folio)) { + err =PTR_ERR (folio); + goto out_iput; + }if (!check_valid_map (F2FS_I_SB (inode), segno, off)) { err = -ENOENT ;
@@ -1473,6 +1504,9 @@ static int move_data_block(struct inode *inode, block_t bidx,folio_unlock (folio);folio_end_dropbehind (folio);folio_put (folio); +out_iput: +if (atomic_inode) +iput (atomic_inode);return err; }
fs/f2fs/inode.c +8 -3
@@ -863,10 +863,15 @@ void f2fs_evict_inode(struct inode *inode)f2fs_abort_atomic_write (inode, true);if (fi->cow_inode &&f2fs_is_cow_file (fi->cow_inode)) { -clear_inode_flag (fi->cow_inode,FI_COW_FILE ); -F2FS_I (fi->cow_inode)->atomic_inode =NULL ; -iput (fi->cow_inode); +struct inode *cow_inode = fi->cow_inode; + +f2fs_down_write (&F2FS_I (cow_inode)->i_sem); +clear_inode_flag (cow_inode,FI_COW_FILE ); +F2FS_I (cow_inode)->atomic_inode =NULL ; fi->cow_inode =NULL ; +f2fs_up_write (&F2FS_I (cow_inode)->i_sem); + +iput (cow_inode); }trace_f2fs_evict_inode (inode);