- 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.

Cc: [email protected]
Cc: Daeho Jeong <[email protected]>
Cc: Sunmin Jeong <[email protected]>
Fixes: 3db1de0e582c ("f2fs: change the current atomic write way")
Fixes: f18d00769336 ("f2fs: use meta inode for GC of COW file")
Signed-off-by: Chao Yu <[email protected]>
---
 fs/f2fs/gc.c    | 50 +++++++++++++++++++++++++++++++++++++++++--------
 fs/f2fs/inode.c | 11 ++++++++---
 2 files changed, 50 insertions(+), 11 deletions(-)

diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 3048cc47b5e0..0e537508df20 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -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;
 }
 
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 12f982f87f15..315682e5da53 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -875,10 +875,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);
-- 
2.49.0



_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

Reply via email to