]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Tue, 12 May 2026 14:35:34 +0000 (10:35 -0400)
committerSasha Levin <sashal@kernel.org>
Tue, 12 May 2026 14:35:34 +0000 (10:35 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-6.1/f2fs-compress-change-the-first-parameter-of-page_arr.patch [new file with mode: 0644]
queue-6.1/f2fs-compress-fix-uaf-of-f2fs_inode_info-in-f2fs_fre.patch [new file with mode: 0644]
queue-6.1/series

diff --git a/queue-6.1/f2fs-compress-change-the-first-parameter-of-page_arr.patch b/queue-6.1/f2fs-compress-change-the-first-parameter-of-page_arr.patch
new file mode 100644 (file)
index 0000000..ccda2c6
--- /dev/null
@@ -0,0 +1,196 @@
+From 4e9ef60e9ca5da5e617b13449657c7569a38e1ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 15:50:09 +0800
+Subject: f2fs: compress: change the first parameter of page_array_{alloc,free}
+ to sbi
+
+From: Zhiguo Niu <zhiguo.niu@unisoc.com>
+
+[ Upstream commit 8e2a9b656474d67c55010f2c003ea2cf889a19ff ]
+
+No logic changes, just cleanup and prepare for fixing the UAF issue
+in f2fs_free_dic.
+
+Signed-off-by: Zhiguo Niu <zhiguo.niu@unisoc.com>
+Signed-off-by: Baocong Liu <baocong.liu@unisoc.com>
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Bin Lan <lanbincn@139.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/f2fs/compress.c | 40 ++++++++++++++++++++--------------------
+ 1 file changed, 20 insertions(+), 20 deletions(-)
+
+diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
+index 4dcd0870e0c74..1e90286212866 100644
+--- a/fs/f2fs/compress.c
++++ b/fs/f2fs/compress.c
+@@ -23,20 +23,18 @@
+ static struct kmem_cache *cic_entry_slab;
+ static struct kmem_cache *dic_entry_slab;
+-static void *page_array_alloc(struct inode *inode, int nr)
++static void *page_array_alloc(struct f2fs_sb_info *sbi, int nr)
+ {
+-      struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+       unsigned int size = sizeof(struct page *) * nr;
+       if (likely(size <= sbi->page_array_slab_size))
+               return f2fs_kmem_cache_alloc(sbi->page_array_slab,
+-                                      GFP_F2FS_ZERO, false, F2FS_I_SB(inode));
++                                      GFP_F2FS_ZERO, false, sbi);
+       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)
+@@ -145,13 +143,13 @@ int f2fs_init_compress_ctx(struct compress_ctx *cc)
+       if (cc->rpages)
+               return 0;
+-      cc->rpages = page_array_alloc(cc->inode, cc->cluster_size);
++      cc->rpages = page_array_alloc(F2FS_I_SB(cc->inode), cc->cluster_size);
+       return cc->rpages ? 0 : -ENOMEM;
+ }
+ 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;
+@@ -640,6 +638,7 @@ static void *f2fs_vmap(struct page **pages, unsigned int count)
+ static int f2fs_compress_pages(struct compress_ctx *cc)
+ {
++      struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
+       struct f2fs_inode_info *fi = F2FS_I(cc->inode);
+       const struct f2fs_compress_ops *cops =
+                               f2fs_cops[fi->i_compress_algorithm];
+@@ -660,7 +659,7 @@ static int f2fs_compress_pages(struct compress_ctx *cc)
+       cc->nr_cpages = DIV_ROUND_UP(max_len, PAGE_SIZE);
+       cc->valid_nr_cpages = cc->nr_cpages;
+-      cc->cpages = page_array_alloc(cc->inode, cc->nr_cpages);
++      cc->cpages = page_array_alloc(sbi, cc->nr_cpages);
+       if (!cc->cpages) {
+               ret = -ENOMEM;
+               goto destroy_compress_ctx;
+@@ -742,7 +741,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(sbi, cc->cpages, cc->nr_cpages);
+       cc->cpages = NULL;
+ destroy_compress_ctx:
+       if (cops->destroy_compress_ctx)
+@@ -1308,7 +1307,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
+       cic->magic = F2FS_COMPRESSED_PAGE_MAGIC;
+       cic->inode = inode;
+       atomic_set(&cic->pending_pages, cc->valid_nr_cpages);
+-      cic->rpages = page_array_alloc(cc->inode, cc->cluster_size);
++      cic->rpages = page_array_alloc(sbi, cc->cluster_size);
+       if (!cic->rpages)
+               goto out_put_cic;
+@@ -1401,13 +1400,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(sbi, 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(sbi, cic->rpages, cc->cluster_size);
+       for (--i; i >= 0; i--)
+               fscrypt_finalize_bounce_page(&cc->cpages[i]);
+@@ -1425,7 +1424,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(sbi, cc->cpages, cc->nr_cpages);
+       cc->cpages = NULL;
+       return -EAGAIN;
+ }
+@@ -1455,7 +1454,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(sbi, cic->rpages, cic->nr_rpages);
+       kmem_cache_free(cic_entry_slab, cic);
+       /*
+@@ -1601,7 +1600,7 @@ static int f2fs_prepare_decomp_mem(struct decompress_io_ctx *dic,
+       if (!allow_memalloc_for_decomp(F2FS_I_SB(dic->inode), pre_alloc))
+               return 0;
+-      dic->tpages = page_array_alloc(dic->inode, dic->cluster_size);
++      dic->tpages = page_array_alloc(F2FS_I_SB(dic->inode), dic->cluster_size);
+       if (!dic->tpages)
+               return -ENOMEM;
+@@ -1663,7 +1662,7 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
+       if (!dic)
+               return ERR_PTR(-ENOMEM);
+-      dic->rpages = page_array_alloc(cc->inode, cc->cluster_size);
++      dic->rpages = page_array_alloc(sbi, cc->cluster_size);
+       if (!dic->rpages) {
+               kmem_cache_free(dic_entry_slab, dic);
+               return ERR_PTR(-ENOMEM);
+@@ -1684,7 +1683,7 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
+               dic->rpages[i] = cc->rpages[i];
+       dic->nr_rpages = cc->cluster_size;
+-      dic->cpages = page_array_alloc(dic->inode, dic->nr_cpages);
++      dic->cpages = page_array_alloc(sbi, dic->nr_cpages);
+       if (!dic->cpages) {
+               ret = -ENOMEM;
+               goto out_free;
+@@ -1719,6 +1718,7 @@ static void f2fs_free_dic(struct decompress_io_ctx *dic,
+               bool bypass_destroy_callback)
+ {
+       int i;
++      struct f2fs_sb_info *sbi = F2FS_I_SB(dic->inode);
+       f2fs_release_decomp_mem(dic, bypass_destroy_callback, true);
+@@ -1730,7 +1730,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(sbi, dic->tpages, dic->cluster_size);
+       }
+       if (dic->cpages) {
+@@ -1739,10 +1739,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(sbi, dic->cpages, dic->nr_cpages);
+       }
+-      page_array_free(dic->inode, dic->rpages, dic->nr_rpages);
++      page_array_free(sbi, dic->rpages, dic->nr_rpages);
+       kmem_cache_free(dic_entry_slab, dic);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/f2fs-compress-fix-uaf-of-f2fs_inode_info-in-f2fs_fre.patch b/queue-6.1/f2fs-compress-fix-uaf-of-f2fs_inode_info-in-f2fs_fre.patch
new file mode 100644 (file)
index 0000000..66ca851
--- /dev/null
@@ -0,0 +1,233 @@
+From d4f6e8256fcab674acc8e3f0bcda071dcf451a18 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 15:50:10 +0800
+Subject: f2fs: compress: fix UAF of f2fs_inode_info in f2fs_free_dic
+
+From: Zhiguo Niu <zhiguo.niu@unisoc.com>
+
+[ Upstream commit 39868685c2a94a70762bc6d77dc81d781d05bff5 ]
+
+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 store compress_algorithm and sbi in dic to avoid inode UAF.
+
+In addition, the previous solution is deprecated in [1] may cause system hang.
+[1] https://lore.kernel.org/all/c36ab955-c8db-4a8b-a9d0-f07b5f426c3f@kernel.org
+
+Cc: Daeho Jeong <daehojeong@google.com>
+Fixes: bff139b49d9f ("f2fs: handle decompress only post processing in softirq")
+Signed-off-by: Zhiguo Niu <zhiguo.niu@unisoc.com>
+Signed-off-by: Baocong Liu <baocong.liu@unisoc.com>
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+[ No changes are made to the code logic; F2FS_I_SB(dic->inode) is
+replaced with dic->sbi in v6.1. ]
+Signed-off-by: Bin Lan <lanbincn@139.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/f2fs/compress.c | 40 ++++++++++++++++++++--------------------
+ fs/f2fs/f2fs.h     |  2 ++
+ 2 files changed, 22 insertions(+), 20 deletions(-)
+
+diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
+index 1e90286212866..5d0c41abb4050 100644
+--- a/fs/f2fs/compress.c
++++ b/fs/f2fs/compress.c
+@@ -211,14 +211,14 @@ static int lzo_decompress_pages(struct decompress_io_ctx *dic)
+                                               dic->rbuf, &dic->rlen);
+       if (ret != LZO_E_OK) {
+               printk_ratelimited("%sF2FS-fs (%s): lzo decompress failed, ret:%d\n",
+-                              KERN_ERR, F2FS_I_SB(dic->inode)->sb->s_id, ret);
++                              KERN_ERR, dic->sbi->sb->s_id, ret);
+               return -EIO;
+       }
+       if (dic->rlen != PAGE_SIZE << dic->log_cluster_size) {
+               printk_ratelimited("%sF2FS-fs (%s): lzo invalid rlen:%zu, "
+                                       "expected:%lu\n", KERN_ERR,
+-                                      F2FS_I_SB(dic->inode)->sb->s_id,
++                                      dic->sbi->sb->s_id,
+                                       dic->rlen,
+                                       PAGE_SIZE << dic->log_cluster_size);
+               return -EIO;
+@@ -307,14 +307,14 @@ static int lz4_decompress_pages(struct decompress_io_ctx *dic)
+                                               dic->clen, dic->rlen);
+       if (ret < 0) {
+               printk_ratelimited("%sF2FS-fs (%s): lz4 decompress failed, ret:%d\n",
+-                              KERN_ERR, F2FS_I_SB(dic->inode)->sb->s_id, ret);
++                              KERN_ERR, dic->sbi->sb->s_id, ret);
+               return -EIO;
+       }
+       if (ret != PAGE_SIZE << dic->log_cluster_size) {
+               printk_ratelimited("%sF2FS-fs (%s): lz4 invalid ret:%d, "
+                                       "expected:%lu\n", KERN_ERR,
+-                                      F2FS_I_SB(dic->inode)->sb->s_id, ret,
++                                      dic->sbi->sb->s_id, ret,
+                                       PAGE_SIZE << dic->log_cluster_size);
+               return -EIO;
+       }
+@@ -437,7 +437,7 @@ static int zstd_init_decompress_ctx(struct decompress_io_ctx *dic)
+       workspace_size = zstd_dstream_workspace_bound(max_window_size);
+-      workspace = f2fs_kvmalloc(F2FS_I_SB(dic->inode),
++      workspace = f2fs_kvmalloc(dic->sbi,
+                                       workspace_size, GFP_NOFS);
+       if (!workspace)
+               return -ENOMEM;
+@@ -445,7 +445,7 @@ static int zstd_init_decompress_ctx(struct decompress_io_ctx *dic)
+       stream = zstd_init_dstream(max_window_size, workspace, workspace_size);
+       if (!stream) {
+               printk_ratelimited("%sF2FS-fs (%s): %s zstd_init_dstream failed\n",
+-                              KERN_ERR, F2FS_I_SB(dic->inode)->sb->s_id,
++                              KERN_ERR, dic->sbi->sb->s_id,
+                               __func__);
+               kvfree(workspace);
+               return -EIO;
+@@ -482,7 +482,7 @@ static int zstd_decompress_pages(struct decompress_io_ctx *dic)
+       ret = zstd_decompress_stream(stream, &outbuf, &inbuf);
+       if (zstd_is_error(ret)) {
+               printk_ratelimited("%sF2FS-fs (%s): %s zstd_decompress_stream failed, ret: %d\n",
+-                              KERN_ERR, F2FS_I_SB(dic->inode)->sb->s_id,
++                              KERN_ERR, dic->sbi->sb->s_id,
+                               __func__, zstd_get_error_code(ret));
+               return -EIO;
+       }
+@@ -490,7 +490,7 @@ static int zstd_decompress_pages(struct decompress_io_ctx *dic)
+       if (dic->rlen != outbuf.pos) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD invalid rlen:%zu, "
+                               "expected:%lu\n", KERN_ERR,
+-                              F2FS_I_SB(dic->inode)->sb->s_id,
++                              dic->sbi->sb->s_id,
+                               __func__, dic->rlen,
+                               PAGE_SIZE << dic->log_cluster_size);
+               return -EIO;
+@@ -759,7 +759,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];
+@@ -832,7 +832,7 @@ void f2fs_end_read_compressed_page(struct page *page, bool failed,
+ {
+       struct decompress_io_ctx *dic =
+                       (struct decompress_io_ctx *)page_private(page);
+-      struct f2fs_sb_info *sbi = F2FS_I_SB(dic->inode);
++      struct f2fs_sb_info *sbi = dic->sbi;
+       dec_page_count(sbi, F2FS_RD_DATA);
+@@ -1593,14 +1593,13 @@ static inline bool allow_memalloc_for_decomp(struct f2fs_sb_info *sbi,
+ static int f2fs_prepare_decomp_mem(struct decompress_io_ctx *dic,
+               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];
+       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(F2FS_I_SB(dic->inode), dic->cluster_size);
++      dic->tpages = page_array_alloc(dic->sbi, dic->cluster_size);
+       if (!dic->tpages)
+               return -ENOMEM;
+@@ -1632,10 +1631,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)
+@@ -1670,6 +1668,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;
+@@ -1718,7 +1718,8 @@ static void f2fs_free_dic(struct decompress_io_ctx *dic,
+               bool bypass_destroy_callback)
+ {
+       int i;
+-      struct f2fs_sb_info *sbi = F2FS_I_SB(dic->inode);
++      /* use sbi in dic to avoid UFA of dic->inode*/
++      struct f2fs_sb_info *sbi = dic->sbi;
+       f2fs_release_decomp_mem(dic, bypass_destroy_callback, true);
+@@ -1761,8 +1762,7 @@ static void f2fs_put_dic(struct decompress_io_ctx *dic, bool in_task)
+                       f2fs_free_dic(dic, false);
+               } else {
+                       INIT_WORK(&dic->free_work, f2fs_late_free_dic);
+-                      queue_work(F2FS_I_SB(dic->inode)->post_read_wq,
+-                                      &dic->free_work);
++                      queue_work(dic->sbi->post_read_wq, &dic->free_work);
+               }
+       }
+ }
+diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
+index bbb86e2156989..faa6efe1ceaf5 100644
+--- a/fs/f2fs/f2fs.h
++++ b/fs/f2fs/f2fs.h
+@@ -1546,6 +1546,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 */
+@@ -1586,6 +1587,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 */
+-- 
+2.53.0
+
index 1051169a231a482d30c9cd0ad876478a136c9a45..b4c0454293051455be5faff5c1d60f9f173eeb35 100644 (file)
@@ -383,3 +383,5 @@ f2fs-fix-incorrect-multidevice-info-in-trace_f2fs_map_blocks.patch
 kvm-arm64-vgic-fix-iidr-revision-field-extracted-from-wrong-value.patch
 loongarch-fix-potential-ade-in-loongson_gpu_fixup_dma_hang.patch
 loongarch-use-per-root-bridge-pcih-flag-to-skip-mem-resource-fixup.patch
+f2fs-compress-change-the-first-parameter-of-page_arr.patch
+f2fs-compress-fix-uaf-of-f2fs_inode_info-in-f2fs_fre.patch