Zhiguo Niu <niuzhigu...@gmail.com> 于2025年6月11日周三 14:52写道:
>
> Chao Yu <c...@kernel.org> 于2025年6月11日周三 14:47写道:
> >
> > On 6/11/25 14:41, Zhiguo Niu wrote:
> > > Chao Yu <c...@kernel.org> 于2025年6月11日周三 14:07写道:
> > >>
> > >> On 6/11/25 00:08, Jaegeuk Kim wrote:
> > >>> Hi Zhiguo,
> > >>>
> > >>> This patch causes CPU hang when running fsstress on 
> > >>> compressed/non-compressed
> > >>> files. Please check.
> > >>
> > >> Oh, seems it may cause below deadlock:
> > >>
> > >> CPU0
> > >> process A
> > >> - spin_lock(i_lock)
> > >> software IRQ
> > >> - end_io
> > >>  - igrab
> > >>   - spin_lock(i_lock)
> > >>
> > >> Thanks,
> > > Hi Chao,
> > > Thanks for pointing this out.
> > > I have tested this patch locally about some basic cases before submission.
> > > So it seems that should use the following method  to solve this problem?
> > > " store i_compress_algorithm/sbi in dic to avoid inode access?"
> >
> > Zhiguo,
> >
> > Yeah, I guess so.
> Hi Chao,
> OK, I will prepare it .
> Thanks a lot.
> >
> > Thanks,
> >
> > > thanks!
> > >
> > >
> > >>
> > >>>
> > >>> On 06/05, Zhiguo Niu wrote:
> > >>>> The decompress_io_ctx may be released asynchronously after
> > >>>> I/O completion. If this file is deleted immediately after read,
> > >>>> and the kworker of processing post_read_wq has not been executed yet
> > >>>> due to high workloads, It is possible that the inode(f2fs_inode_info)
> > >>>> is evicted and freed before it is used f2fs_free_dic.
> > >>>>
> > >>>>     The UAF case as below:
> > >>>>     Thread A                                      Thread B
> > >>>>     - f2fs_decompress_end_io
> > >>>>      - f2fs_put_dic
> > >>>>       - queue_work
> > >>>>         add free_dic work to post_read_wq
> > >>>>                                                    - do_unlink
> > >>>>                                                     - iput
> > >>>>                                                      - evict
> > >>>>                                                       - call_rcu
> > >>>>     This file is deleted after read.
> > >>>>
> > >>>>     Thread C                                 kworker to process 
> > >>>> post_read_wq
> > >>>>     - rcu_do_batch
> > >>>>      - f2fs_free_inode
> > >>>>       - kmem_cache_free
> > >>>>      inode is freed by rcu
> > >>>>                                              - process_scheduled_works
> > >>>>                                               - f2fs_late_free_dic
> > >>>>                                                - f2fs_free_dic
> > >>>>                                                 - 
> > >>>> f2fs_release_decomp_mem
> > >>>>                                       read 
> > >>>> (dic->inode)->i_compress_algorithm
> > >>>>
> > >>>> This patch use igrab before f2fs_free_dic and iput after free the dic 
> > >>>> when dic free
> > >>>> action is done by kworker.
> > >>>>
> > >>>> Cc: Daeho Jeong <daehoje...@google.com>
> > >>>> Fixes: bff139b49d9f ("f2fs: handle decompress only post processing in 
> > >>>> softirq")
> > >>>> Signed-off-by: Zhiguo Niu <zhiguo....@unisoc.com>
> > >>>> Signed-off-by: Baocong Liu <baocong....@unisoc.com>
> > >>>> ---
> > >>>> v3: use igrab to replace __iget
> > >>>> v2: use __iget/iput function
> > >>>> ---
> > >>>>  fs/f2fs/compress.c | 14 +++++++++-----
> > >>>>  1 file changed, 9 insertions(+), 5 deletions(-)
> > >>>>
> > >>>> diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
> > >>>> index b3c1df9..729ad16 100644
> > >>>> --- a/fs/f2fs/compress.c
> > >>>> +++ b/fs/f2fs/compress.c
> > >>>> @@ -1687,7 +1687,7 @@ static void f2fs_release_decomp_mem(struct 
> > >>>> decompress_io_ctx *dic,
> > >>>>  }
> > >>>>
> > >>>>  static void f2fs_free_dic(struct decompress_io_ctx *dic,
> > >>>> -            bool bypass_destroy_callback);
> > >>>> +            bool bypass_destroy_callback, bool late_free);
> > >>>>
> > >>>>  struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
> > >>>>  {
> > >>>> @@ -1743,12 +1743,12 @@ struct decompress_io_ctx 
> > >>>> *f2fs_alloc_dic(struct compress_ctx *cc)
> > >>>>      return dic;
> > >>>>
> > >>>>  out_free:
> > >>>> -    f2fs_free_dic(dic, true);
> > >>>> +    f2fs_free_dic(dic, true, false);
> > >>>>      return ERR_PTR(ret);
> > >>>>  }
> > >>>>
> > >>>>  static void f2fs_free_dic(struct decompress_io_ctx *dic,
> > >>>> -            bool bypass_destroy_callback)
> > >>>> +            bool bypass_destroy_callback, bool late_free)
> > >>>>  {
> > >>>>      int i;
> > >>>>
> > >>>> @@ -1775,6 +1775,8 @@ static void f2fs_free_dic(struct 
> > >>>> decompress_io_ctx *dic,
> > >>>>      }
> > >>>>
> > >>>>      page_array_free(dic->inode, dic->rpages, dic->nr_rpages);
> > >>>> +    if (late_free)
> > >>>> +            iput(dic->inode);
> > >>>>      kmem_cache_free(dic_entry_slab, dic);
> > >>>>  }
> > >>>>
> > >>>> @@ -1783,16 +1785,18 @@ static void f2fs_late_free_dic(struct 
> > >>>> work_struct *work)
> > >>>>      struct decompress_io_ctx *dic =
> > >>>>              container_of(work, struct decompress_io_ctx, free_work);
> > >>>>
> > >>>> -    f2fs_free_dic(dic, false);
> > >>>> +    f2fs_free_dic(dic, false, true);
> > >>>>  }
> > >>>>
> > >>>>  static void f2fs_put_dic(struct decompress_io_ctx *dic, bool in_task)
> > >>>>  {
> > >>>>      if (refcount_dec_and_test(&dic->refcnt)) {
> > >>>>              if (in_task) {
> > >>>> -                    f2fs_free_dic(dic, false);
> > >>>> +                    f2fs_free_dic(dic, false, false);
> > >>>>              } else {
> > >>>>                      INIT_WORK(&dic->free_work, f2fs_late_free_dic);
> > >>>> +                    /* use igrab to avoid inode is evicted 
> > >>>> simultaneously */
> > >>>> +                    f2fs_bug_on(F2FS_I_SB(dic->inode), 
> > >>>> !igrab(dic->inode));
> > >>>>                      queue_work(F2FS_I_SB(dic->inode)->post_read_wq,
> > >>>>                                      &dic->free_work);
> > >>>>              }
> > >>>> --
> > >>>> 1.9.1
> > >>
> >

Hi Chao,

The patch is about as follows, because dic->sbi is used directly in
f2fs_free_dic: page_array_free(dic->sbi, dic->tpages, dic->cluster_size);
so there are two points I want to confirm:
1. As a corresponding, the first parameter (inode) of page_array_alloc
is need to modify to sbi or not ?
2. As a corresponding, do we need to add the sbi field in compress_ctx
so that page_array_alloc/free called
in compress flow can use sbi directly?
Thanks!

diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index b3c1df9..897d8ae 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -34,9 +34,8 @@ static void *page_array_alloc(struct inode *inode, int nr)
        return f2fs_kzalloc(sbi, size, GFP_NOFS);
 }

-static void page_array_free(struct inode *inode, void *pages, int nr)
+static void page_array_free(struct f2fs_sb_info *sbi, void *pages, int nr)
 {
-       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        unsigned int size = sizeof(struct page *) * nr;

        if (!pages)
@@ -155,7 +154,7 @@ int f2fs_init_compress_ctx(struct compress_ctx *cc)

 void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse)
 {
-       page_array_free(cc->inode, cc->rpages, cc->cluster_size);
+       page_array_free(F2FS_I_SB(cc->inode), cc->rpages, cc->cluster_size);
        cc->rpages = NULL;
        cc->nr_rpages = 0;
        cc->nr_cpages = 0;
@@ -716,7 +715,7 @@ static int f2fs_compress_pages(struct compress_ctx *cc)
                if (cc->cpages[i])
                        f2fs_compress_free_page(cc->cpages[i]);
        }
-       page_array_free(cc->inode, cc->cpages, cc->nr_cpages);
+       page_array_free(F2FS_I_SB(cc->inode), cc->cpages, cc->nr_cpages);
        cc->cpages = NULL;
 destroy_compress_ctx:
        if (cops->destroy_compress_ctx)
@@ -734,7 +733,7 @@ static void f2fs_release_decomp_mem(struct
decompress_io_ctx *dic,

 void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task)
 {
-       struct f2fs_sb_info *sbi = F2FS_I_SB(dic->inode);
+       struct f2fs_sb_info *sbi = dic->sbi;
        struct f2fs_inode_info *fi = F2FS_I(dic->inode);
        const struct f2fs_compress_ops *cops =
                        f2fs_cops[fi->i_compress_algorithm];
@@ -1442,13 +1441,13 @@ static int f2fs_write_compressed_pages(struct
compress_ctx *cc,
        spin_unlock(&fi->i_size_lock);

        f2fs_put_rpages(cc);
-       page_array_free(cc->inode, cc->cpages, cc->nr_cpages);
+       page_array_free(F2FS_I_SB(cc->inode), cc->cpages, cc->nr_cpages);
        cc->cpages = NULL;
        f2fs_destroy_compress_ctx(cc, false);
        return 0;

 out_destroy_crypt:
-       page_array_free(cc->inode, cic->rpages, cc->cluster_size);
+       page_array_free(F2FS_I_SB(cc->inode), cic->rpages, cc->cluster_size);

        for (--i; i >= 0; i--) {
                if (!cc->cpages[i])
@@ -1469,7 +1468,7 @@ static int f2fs_write_compressed_pages(struct
compress_ctx *cc,
                f2fs_compress_free_page(cc->cpages[i]);
                cc->cpages[i] = NULL;
        }
-       page_array_free(cc->inode, cc->cpages, cc->nr_cpages);
+       page_array_free(F2FS_I_SB(cc->inode), cc->cpages, cc->nr_cpages);
        cc->cpages = NULL;
        return -EAGAIN;
 }
@@ -1499,7 +1498,7 @@ void f2fs_compress_write_end_io(struct bio *bio,
struct page *page)
                end_page_writeback(cic->rpages[i]);
        }

-       page_array_free(cic->inode, cic->rpages, cic->nr_rpages);
+       page_array_free(F2FS_I_SB(cic->inode), cic->rpages, cic->nr_rpages);
        kmem_cache_free(cic_entry_slab, cic);
 }

@@ -1637,7 +1636,7 @@ static int f2fs_prepare_decomp_mem(struct
decompress_io_ctx *dic,
                f2fs_cops[F2FS_I(dic->inode)->i_compress_algorithm];
        int i;

-       if (!allow_memalloc_for_decomp(F2FS_I_SB(dic->inode), pre_alloc))
+       if (!allow_memalloc_for_decomp(dic->sbi, pre_alloc))
                return 0;

        dic->tpages = page_array_alloc(dic->inode, dic->cluster_size);
@@ -1670,10 +1669,9 @@ static int f2fs_prepare_decomp_mem(struct
decompress_io_ctx *dic,
 static void f2fs_release_decomp_mem(struct decompress_io_ctx *dic,
                bool bypass_destroy_callback, bool pre_alloc)
 {
-       const struct f2fs_compress_ops *cops =
-               f2fs_cops[F2FS_I(dic->inode)->i_compress_algorithm];
+       const struct f2fs_compress_ops *cops =
f2fs_cops[dic->compress_algorithm];

-       if (!allow_memalloc_for_decomp(F2FS_I_SB(dic->inode), pre_alloc))
+       if (!allow_memalloc_for_decomp(dic->sbi, pre_alloc))
                return;

        if (!bypass_destroy_callback && cops->destroy_decompress_ctx)
@@ -1708,6 +1706,8 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct
compress_ctx *cc)

        dic->magic = F2FS_COMPRESSED_PAGE_MAGIC;
        dic->inode = cc->inode;
+       dic->sbi = sbi;
+       dic->compress_algorithm = F2FS_I(cc->inode)->i_compress_algorithm;
        atomic_set(&dic->remaining_pages, cc->nr_cpages);
        dic->cluster_idx = cc->cluster_idx;
        dic->cluster_size = cc->cluster_size;
@@ -1762,7 +1762,7 @@ static void f2fs_free_dic(struct decompress_io_ctx *dic,
                                continue;
                        f2fs_compress_free_page(dic->tpages[i]);
                }
-               page_array_free(dic->inode, dic->tpages, dic->cluster_size);
+               page_array_free(dic->sbi, dic->tpages, dic->cluster_size);
        }

        if (dic->cpages) {
@@ -1771,10 +1771,10 @@ static void f2fs_free_dic(struct decompress_io_ctx *dic,
                                continue;
                        f2fs_compress_free_page(dic->cpages[i]);
                }
-               page_array_free(dic->inode, dic->cpages, dic->nr_cpages);
+               page_array_free(dic->sbi, dic->cpages, dic->nr_cpages);
        }

-       page_array_free(dic->inode, dic->rpages, dic->nr_rpages);
+       page_array_free(dic->sbi, dic->rpages, dic->nr_rpages);
        kmem_cache_free(dic_entry_slab, dic);
 }

diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 9333a22b..da2137e 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1536,6 +1536,7 @@ struct compress_io_ctx {
 struct decompress_io_ctx {
        u32 magic;                      /* magic number to indicate
page is compressed */
        struct inode *inode;            /* inode the context belong to */
+       struct f2fs_sb_info *sbi;       /* f2fs_sb_info pointer */
        pgoff_t cluster_idx;            /* cluster index number */
        unsigned int cluster_size;      /* page count in cluster */
        unsigned int log_cluster_size;  /* log of cluster size */
@@ -1576,6 +1577,7 @@ struct decompress_io_ctx {

        bool failed;                    /* IO error occurred before
decompression? */
        bool need_verity;               /* need fs-verity verification
after decompression? */
+       unsigned char compress_algorithm;       /* backup algorithm type */
        void *private;                  /* payload buffer for
specified decompression algorithm */
        void *private2;                 /* extra payload buffer */
        struct work_struct verity_work; /* work to verify the
decompressed pages */


_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

Reply via email to