public inbox for linux-f2fs-devel@lists.sourceforge.net
 help / color / mirror / Atom feed
* [f2fs-dev] [PATCH] f2fs: fix use-after-free in f2fs_compress_write_end_io()
@ 2026-03-22 21:31 G S
  2026-03-23  7:46 ` Greg KH
  0 siblings, 1 reply; 11+ messages in thread
From: G S @ 2026-03-22 21:31 UTC (permalink / raw)
  To: security; +Cc: jaegeuk, linux-f2fs-devel

Hi,

I found a use-after-free in f2fs_compress_write_end_io() that is the
same class of bug as CVE-2026-23234 (UAF in f2fs_write_end_io()) but
in the compressed page write completion path. The CVE-2026-23234 fix
does not cover this function.

Bug description
---------------
In f2fs_compress_write_end_io() (fs/f2fs/compress.c:1478), the
completion callback accesses sbi after an operation that allows a
concurrent unmount to free it.

The unmount path in f2fs_put_super() (super.c:1985) calls:
    f2fs_wait_on_all_pages(sbi, F2FS_WB_CP_DATA);

This waits until get_pages(sbi, F2FS_WB_CP_DATA) returns 0, then
proceeds to tear down sbi (super.c:2028-2030, kfree at 5377/5458).

Compressed writeback bios use type F2FS_WB_CP_DATA (because
WB_DATA_TYPE evaluates f2fs_is_compressed_page() as true at
compress.c:1483-1484). This means f2fs_wait_on_all_pages structurally
waits for all compressed bio completions to call dec_page_count()
before unmount proceeds. A bio cannot complete after sbi is freed
via the normal path — the wait loop creates an ordering dependency.

However, the ordering only guarantees that dec_page_count() has run.
It does NOT guarantee that the completion callback has finished all
subsequent sbi accesses. The race is:

    f2fs_compress_write_end_io():
      sbi = bio->bi_private               // line 1481
      dec_page_count(sbi, type)            // line 1492 — decrements counter
        → if this is the last bio, counter hits 0
        → f2fs_wait_on_all_pages() on unmount CPU returns
        → f2fs_put_super() continues:
            f2fs_destroy_page_array_cache(sbi)  // super.c:2030
            kfree(sbi)                          // super.c:5377 or 5458
      [callback still executing on this CPU:]
      for (i = 0; ...) {
          end_page_writeback(cic->rpages[i])    // line 1500
      }
      page_array_free(sbi, ...)                 // line 1503 — UAF

Once dec_page_count() brings the F2FS_WB_CP_DATA counter to zero,
f2fs_wait_on_all_pages returns on the unmount CPU, and f2fs_put_super
proceeds to free sbi. The completion callback on the bio CPU is still
between lines 1492 and 1503, and the subsequent page_array_free(sbi,
...) dereferences sbi->page_array_slab_size (comparison at
compress.c:43) and sbi->page_array_slab (passed to kmem_cache_free
at compress.c:44), both within the freed f2fs_sb_info structure.

Note: dec_page_count() at line 1492 runs for EVERY compressed folio
in the bio, but page_array_free() at line 1503 only runs when
atomic_dec_return(&cic->pending_pages) reaches zero. The race
specifically requires the LAST bio completion for a given cic —
earlier completions return at line 1495 and never reach
page_array_free.

Impact
------
f2fs with compression enabled (compress_algorithm=lz4/zstd) is the
default on Android devices (Pixel 6+, Samsung Galaxy S21+). Any
unprivileged app performing file I/O on a compressed f2fs directory
triggers compressed writeback. The UAF is on f2fs_sb_info which is a
large structure containing slab cache pointers, making heap-spray-based
exploitation feasible for privilege escalation.

Affected versions
-----------------
All kernels since f2fs compression writeback support was introduced
(v5.6+, commit 4c8ff709c6 "f2fs: support data compression") through
at least v6.19. The CVE-2026-23234 patch for f2fs_write_end_io() does
not fix this function.

Note: f2fs_write_end_io() in data.c:317 has the same structural problem
for non-compressed folios. CVE-2026-23234 addressed it there. Both
functions share the root cause: missing lifetime management on sbi
across async bio completion.

Fix
---
The root cause is that after dec_page_count() unblocks unmount, the
callback continues to access sbi via page_array_free(). The narrowest
correct fix is to ensure all sbi accesses in the callback complete
before the counter decrement that signals unmount.

Approach 1 (reorder): Move page_array_free() before dec_page_count().
This is not straightforward because dec_page_count is per-folio but
page_array_free only runs on the last pending page (after the
atomic_dec_return check at line 1494). The per-folio dec_page_count
at line 1492 can unblock unmount before the last-page path reaches
page_array_free at line 1503.

Approach 2 (cache sbi fields): Cache sbi->page_array_slab and
sbi->page_array_slab_size into local variables at function entry
(before dec_page_count), then use the cached values in the
page_array_free equivalent at line 1503. This avoids dereferencing
sbi after it may have been freed:

--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -1478,6 +1478,8 @@ void f2fs_compress_write_end_io(struct bio *bio,
struct folio *folio)
 {
  struct page *page = &folio->page;
  struct f2fs_sb_info *sbi = bio->bi_private;
+ struct kmem_cache *pa_slab = sbi->page_array_slab;
+ unsigned int pa_slab_size = sbi->page_array_slab_size;
  struct compress_io_ctx *cic = folio->private;
  enum count_type type = WB_DATA_TYPE(folio,
  f2fs_is_compressed_page(folio));
@@ -1497,7 +1499,12 @@ void f2fs_compress_write_end_io(struct bio
*bio, struct folio *folio)
  end_page_writeback(cic->rpages[i]);
  }

- page_array_free(sbi, cic->rpages, cic->nr_rpages);
+ /*
+ * Use cached slab fields: after dec_page_count above, unmount may
+ * have freed sbi. This is the compress-path analog of CVE-2026-23234.
+ */
+ if (likely(sizeof(struct page *) * cic->nr_rpages <= pa_slab_size))
+ kmem_cache_free(pa_slab, cic->rpages);
+ else
+ kfree(cic->rpages);
  kmem_cache_free(cic_entry_slab, cic);
 }

This is safe because sbi is still valid at function entry (the
F2FS_WB_CP_DATA counter is nonzero, preventing unmount from
proceeding). The cached values are read before dec_page_count()
at line 1492, which is the operation that can unblock unmount.

Approach 3 (superblock reference): atomic_inc(&sbi->sb->s_active) at
bio allocation (data.c:470), deactivate_super(sbi->sb) at bio
completion (data.c, before bio_put). This is the most robust fix but
note: deactivate_super() can call down_write(&sb->s_umount) — a
sleeping lock — if it drops the last s_active reference. In practice
this is safe because the VFS mount holds an independent s_active
reference until unmount completes, ensuring the bio completion call
is never the last release. Nevertheless, if this approach is chosen,
a comment documenting this invariant should be added.

Fixes: 4c8ff709c6 ("f2fs: support data compression")
Reported-by: George Saad <geoo115@gmail.com>

Thanks,
George


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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [f2fs-dev] [PATCH] f2fs: fix use-after-free in f2fs_compress_write_end_io()
  2026-03-22 21:31 [f2fs-dev] [PATCH] f2fs: fix use-after-free in f2fs_compress_write_end_io() G S
@ 2026-03-23  7:46 ` Greg KH
  2026-03-23  9:03   ` [f2fs-dev] [PATCH] f2fs: fix use-after-free of sbi " George Saad
                     ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Greg KH @ 2026-03-23  7:46 UTC (permalink / raw)
  To: G S; +Cc: jaegeuk, security, linux-f2fs-devel

On Sun, Mar 22, 2026 at 09:31:30PM +0000, G S wrote:
> Hi,
> 
> I found a use-after-free in f2fs_compress_write_end_io() that is the
> same class of bug as CVE-2026-23234 (UAF in f2fs_write_end_io()) but
> in the compressed page write completion path. The CVE-2026-23234 fix
> does not cover this function.

As you have sent this to a public list, no need for security@kernel.org
to get involved.

Also:

> Approach 2 (cache sbi fields): Cache sbi->page_array_slab and
> sbi->page_array_slab_size into local variables at function entry
> (before dec_page_count), then use the cached values in the
> page_array_free equivalent at line 1503. This avoids dereferencing
> sbi after it may have been freed:
> 
> --- a/fs/f2fs/compress.c
> +++ b/fs/f2fs/compress.c
> @@ -1478,6 +1478,8 @@ void f2fs_compress_write_end_io(struct bio *bio,
> struct folio *folio)
>  {
>   struct page *page = &folio->page;
>   struct f2fs_sb_info *sbi = bio->bi_private;
> + struct kmem_cache *pa_slab = sbi->page_array_slab;
> + unsigned int pa_slab_size = sbi->page_array_slab_size;
>   struct compress_io_ctx *cic = folio->private;
>   enum count_type type = WB_DATA_TYPE(folio,
>   f2fs_is_compressed_page(folio));
> @@ -1497,7 +1499,12 @@ void f2fs_compress_write_end_io(struct bio
> *bio, struct folio *folio)
>   end_page_writeback(cic->rpages[i]);
>   }

Can you turn this into a patch that can actually be applied so you get
full credit for the fix?  See
Documentation/process/submitting_patches.rst for how to do that.

thanks,

greg k-h


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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [f2fs-dev] [PATCH] f2fs: fix use-after-free of sbi in f2fs_compress_write_end_io()
  2026-03-23  7:46 ` Greg KH
@ 2026-03-23  9:03   ` George Saad
  2026-03-23  9:32     ` Greg KH
  2026-03-23  9:38   ` [f2fs-dev] [PATCH v2] " George Saad
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 11+ messages in thread
From: George Saad @ 2026-03-23  9:03 UTC (permalink / raw)
  To: Greg KH; +Cc: Jaegeuk Kim, George Saad, linux-f2fs-devel

In f2fs_compress_write_end_io(), dec_page_count(sbi, type) at line 1492
can bring the F2FS_WB_CP_DATA counter to zero, unblocking
f2fs_wait_on_all_pages() in f2fs_put_super() on a concurrent unmount
CPU. The unmount path then proceeds to call
f2fs_destroy_page_array_cache(sbi) and kfree(sbi). Meanwhile, the bio
completion callback is still executing: when it reaches
page_array_free(sbi, ...), it dereferences sbi->page_array_slab_size
and sbi->page_array_slab within the now-freed f2fs_sb_info structure.

This is the same class of bug as CVE-2026-23234 (which fixed the
equivalent race in f2fs_write_end_io() in data.c), but in the
compressed writeback completion path that was not covered by that fix.

Fix this by caching sbi->page_array_slab and sbi->page_array_slab_size
into local variables at function entry, before dec_page_count(). At
function entry, sbi is guaranteed valid because the F2FS_WB_CP_DATA
counter is still nonzero (this invocation has not yet decremented it),
preventing the unmount path from proceeding past
f2fs_wait_on_all_pages(). The cached values are then used in place of
the post-decrement sbi dereference.

Fixes: 4c8ff709c6 ("f2fs: support data compression")
Signed-off-by: George Saad <geoo115@gmail.com>
---
 fs/f2fs/compress.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index 7b68bf229..c3d837df3 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -1479,11 +1479,20 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
 {
 	struct page *page = &folio->page;
 	struct f2fs_sb_info *sbi = bio->bi_private;
+	struct kmem_cache *pa_slab = sbi->page_array_slab;
+	unsigned int pa_slab_size = sbi->page_array_slab_size;
 	struct compress_io_ctx *cic = folio->private;
 	enum count_type type = WB_DATA_TYPE(folio,
 				f2fs_is_compressed_page(folio));
 	int i;
 
+	/*
+	 * Cache sbi fields before dec_page_count(), which may unblock
+	 * f2fs_wait_on_all_pages() in the unmount path, allowing
+	 * f2fs_put_super() to free sbi.  At this point sbi is still
+	 * valid because the F2FS_WB_CP_DATA counter is nonzero.
+	 */
+
 	if (unlikely(bio->bi_status != BLK_STS_OK))
 		mapping_set_error(cic->inode->i_mapping, -EIO);
 
@@ -1500,7 +1509,10 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
 		end_page_writeback(cic->rpages[i]);
 	}
 
-	page_array_free(sbi, cic->rpages, cic->nr_rpages);
+	if (likely(sizeof(struct page *) * cic->nr_rpages <= pa_slab_size))
+		kmem_cache_free(pa_slab, cic->rpages);
+	else
+		kfree(cic->rpages);
 	kmem_cache_free(cic_entry_slab, cic);
 }
 
-- 
2.53.0



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

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [f2fs-dev] [PATCH] f2fs: fix use-after-free of sbi in f2fs_compress_write_end_io()
  2026-03-23  9:03   ` [f2fs-dev] [PATCH] f2fs: fix use-after-free of sbi " George Saad
@ 2026-03-23  9:32     ` Greg KH
  0 siblings, 0 replies; 11+ messages in thread
From: Greg KH @ 2026-03-23  9:32 UTC (permalink / raw)
  To: George Saad; +Cc: Jaegeuk Kim, linux-f2fs-devel

On Mon, Mar 23, 2026 at 09:03:06AM +0000, George Saad wrote:
> In f2fs_compress_write_end_io(), dec_page_count(sbi, type) at line 1492
> can bring the F2FS_WB_CP_DATA counter to zero, unblocking
> f2fs_wait_on_all_pages() in f2fs_put_super() on a concurrent unmount
> CPU. The unmount path then proceeds to call
> f2fs_destroy_page_array_cache(sbi) and kfree(sbi). Meanwhile, the bio
> completion callback is still executing: when it reaches
> page_array_free(sbi, ...), it dereferences sbi->page_array_slab_size
> and sbi->page_array_slab within the now-freed f2fs_sb_info structure.
> 
> This is the same class of bug as CVE-2026-23234 (which fixed the
> equivalent race in f2fs_write_end_io() in data.c), but in the
> compressed writeback completion path that was not covered by that fix.
> 
> Fix this by caching sbi->page_array_slab and sbi->page_array_slab_size
> into local variables at function entry, before dec_page_count(). At
> function entry, sbi is guaranteed valid because the F2FS_WB_CP_DATA
> counter is still nonzero (this invocation has not yet decremented it),
> preventing the unmount path from proceeding past
> f2fs_wait_on_all_pages(). The cached values are then used in place of
> the post-decrement sbi dereference.
> 
> Fixes: 4c8ff709c6 ("f2fs: support data compression")

This commit id is not in Linus's tree, are you sure it is correct?

thanks,

greg k-h


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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [f2fs-dev] [PATCH v2] f2fs: fix use-after-free of sbi in f2fs_compress_write_end_io()
  2026-03-23  7:46 ` Greg KH
  2026-03-23  9:03   ` [f2fs-dev] [PATCH] f2fs: fix use-after-free of sbi " George Saad
@ 2026-03-23  9:38   ` George Saad
  2026-03-23 10:38     ` Greg KH
  2026-03-23 10:44   ` [f2fs-dev] [PATCH v3] " George Saad
  2026-03-23 11:21   ` [f2fs-dev] [PATCH v4] " George Saad
  3 siblings, 1 reply; 11+ messages in thread
From: George Saad @ 2026-03-23  9:38 UTC (permalink / raw)
  To: Greg KH; +Cc: Jaegeuk Kim, George Saad, linux-f2fs-devel

In f2fs_compress_write_end_io(), dec_page_count(sbi, type) at line 1492
can bring the F2FS_WB_CP_DATA counter to zero, unblocking
f2fs_wait_on_all_pages() in f2fs_put_super() on a concurrent unmount
CPU. The unmount path then proceeds to call
f2fs_destroy_page_array_cache(sbi) and kfree(sbi). Meanwhile, the bio
completion callback is still executing: when it reaches
page_array_free(sbi, ...), it dereferences sbi->page_array_slab_size
and sbi->page_array_slab within the now-freed f2fs_sb_info structure.

This is the same class of bug as CVE-2026-23234 (which fixed the
equivalent race in f2fs_write_end_io() in data.c), but in the
compressed writeback completion path that was not covered by that fix.

Fix this by caching sbi->page_array_slab and sbi->page_array_slab_size
into local variables at function entry, before dec_page_count(). At
function entry, sbi is guaranteed valid because the F2FS_WB_CP_DATA
counter is still nonzero (this invocation has not yet decremented it),
preventing the unmount path from proceeding past
f2fs_wait_on_all_pages(). The cached values are then used in place of
the post-decrement sbi dereference.

Fixes: 4c8ff7095bef ("f2fs: support data compression")
Signed-off-by: George Saad <geoo115@gmail.com>
---
 fs/f2fs/compress.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index 7b68bf229..c3d837df3 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -1479,11 +1479,20 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
 {
 	struct page *page = &folio->page;
 	struct f2fs_sb_info *sbi = bio->bi_private;
+	struct kmem_cache *pa_slab = sbi->page_array_slab;
+	unsigned int pa_slab_size = sbi->page_array_slab_size;
 	struct compress_io_ctx *cic = folio->private;
 	enum count_type type = WB_DATA_TYPE(folio,
 				f2fs_is_compressed_page(folio));
 	int i;
 
+	/*
+	 * Cache sbi fields before dec_page_count(), which may unblock
+	 * f2fs_wait_on_all_pages() in the unmount path, allowing
+	 * f2fs_put_super() to free sbi.  At this point sbi is still
+	 * valid because the F2FS_WB_CP_DATA counter is nonzero.
+	 */
+
 	if (unlikely(bio->bi_status != BLK_STS_OK))
 		mapping_set_error(cic->inode->i_mapping, -EIO);
 
@@ -1500,7 +1509,10 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
 		end_page_writeback(cic->rpages[i]);
 	}
 
-	page_array_free(sbi, cic->rpages, cic->nr_rpages);
+	if (likely(sizeof(struct page *) * cic->nr_rpages <= pa_slab_size))
+		kmem_cache_free(pa_slab, cic->rpages);
+	else
+		kfree(cic->rpages);
 	kmem_cache_free(cic_entry_slab, cic);
 }
 
-- 
2.53.0



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

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [f2fs-dev] [PATCH v2] f2fs: fix use-after-free of sbi in f2fs_compress_write_end_io()
  2026-03-23  9:38   ` [f2fs-dev] [PATCH v2] " George Saad
@ 2026-03-23 10:38     ` Greg KH
  0 siblings, 0 replies; 11+ messages in thread
From: Greg KH @ 2026-03-23 10:38 UTC (permalink / raw)
  To: George Saad; +Cc: Jaegeuk Kim, linux-f2fs-devel

On Mon, Mar 23, 2026 at 09:38:28AM +0000, George Saad wrote:
> In f2fs_compress_write_end_io(), dec_page_count(sbi, type) at line 1492
> can bring the F2FS_WB_CP_DATA counter to zero, unblocking
> f2fs_wait_on_all_pages() in f2fs_put_super() on a concurrent unmount
> CPU. The unmount path then proceeds to call
> f2fs_destroy_page_array_cache(sbi) and kfree(sbi). Meanwhile, the bio
> completion callback is still executing: when it reaches
> page_array_free(sbi, ...), it dereferences sbi->page_array_slab_size
> and sbi->page_array_slab within the now-freed f2fs_sb_info structure.
> 
> This is the same class of bug as CVE-2026-23234 (which fixed the
> equivalent race in f2fs_write_end_io() in data.c), but in the
> compressed writeback completion path that was not covered by that fix.
> 
> Fix this by caching sbi->page_array_slab and sbi->page_array_slab_size
> into local variables at function entry, before dec_page_count(). At
> function entry, sbi is guaranteed valid because the F2FS_WB_CP_DATA
> counter is still nonzero (this invocation has not yet decremented it),
> preventing the unmount path from proceeding past
> f2fs_wait_on_all_pages(). The cached values are then used in place of
> the post-decrement sbi dereference.
> 
> Fixes: 4c8ff7095bef ("f2fs: support data compression")
> Signed-off-by: George Saad <geoo115@gmail.com>
> ---
>  fs/f2fs/compress.c | 14 +++++++++++++-
>  1 file changed, 13 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
> index 7b68bf229..c3d837df3 100644
> --- a/fs/f2fs/compress.c
> +++ b/fs/f2fs/compress.c
> @@ -1479,11 +1479,20 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
>  {
>  	struct page *page = &folio->page;
>  	struct f2fs_sb_info *sbi = bio->bi_private;
> +	struct kmem_cache *pa_slab = sbi->page_array_slab;
> +	unsigned int pa_slab_size = sbi->page_array_slab_size;
>  	struct compress_io_ctx *cic = folio->private;
>  	enum count_type type = WB_DATA_TYPE(folio,
>  				f2fs_is_compressed_page(folio));
>  	int i;
>  
> +	/*
> +	 * Cache sbi fields before dec_page_count(), which may unblock
> +	 * f2fs_wait_on_all_pages() in the unmount path, allowing
> +	 * f2fs_put_super() to free sbi.  At this point sbi is still
> +	 * valid because the F2FS_WB_CP_DATA counter is nonzero.
> +	 */
> +
>  	if (unlikely(bio->bi_status != BLK_STS_OK))
>  		mapping_set_error(cic->inode->i_mapping, -EIO);
>  
> @@ -1500,7 +1509,10 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
>  		end_page_writeback(cic->rpages[i]);
>  	}
>  
> -	page_array_free(sbi, cic->rpages, cic->nr_rpages);
> +	if (likely(sizeof(struct page *) * cic->nr_rpages <= pa_slab_size))
> +		kmem_cache_free(pa_slab, cic->rpages);
> +	else
> +		kfree(cic->rpages);
>  	kmem_cache_free(cic_entry_slab, cic);
>  }
>  
> -- 
> 2.53.0
> 

Hi,

This is the friendly patch-bot of Greg Kroah-Hartman.  You have sent him
a patch that has triggered this response.  He used to manually respond
to these common problems, but in order to save his sanity (he kept
writing the same thing over and over, yet to different people), I was
created.  Hopefully you will not take offence and will fix the problem
in your patch and resubmit it so that it can be accepted into the Linux
kernel tree.

You are receiving this message because of the following common error(s)
as indicated below:

- This looks like a new version of a previously submitted patch, but you
  did not list below the --- line any changes from the previous version.
  Please read the section entitled "The canonical patch format" in the
  kernel file, Documentation/process/submitting-patches.rst for what
  needs to be done here to properly describe this.

- You have marked a patch with a "Fixes:" tag for a commit that is in an
  older released kernel, yet you do not have a cc: stable line in the
  signed-off-by area at all, which means that the patch will not be
  applied to any older kernel releases.  To properly fix this, please
  follow the documented rules in the
  Documentation/process/stable-kernel-rules.rst file for how to resolve
  this.

If you wish to discuss this problem further, or you have questions about
how to resolve this issue, please feel free to respond to this email and
Greg will reply once he has dug out from the pending patches received
from other developers.

thanks,

greg k-h's patch email bot


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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [f2fs-dev] [PATCH v3] f2fs: fix use-after-free of sbi in f2fs_compress_write_end_io()
  2026-03-23  7:46 ` Greg KH
  2026-03-23  9:03   ` [f2fs-dev] [PATCH] f2fs: fix use-after-free of sbi " George Saad
  2026-03-23  9:38   ` [f2fs-dev] [PATCH v2] " George Saad
@ 2026-03-23 10:44   ` George Saad
  2026-03-23 11:06     ` Chao Yu via Linux-f2fs-devel
  2026-03-23 11:21   ` [f2fs-dev] [PATCH v4] " George Saad
  3 siblings, 1 reply; 11+ messages in thread
From: George Saad @ 2026-03-23 10:44 UTC (permalink / raw)
  To: Greg KH; +Cc: Jaegeuk Kim, George Saad, stable, linux-f2fs-devel

In f2fs_compress_write_end_io(), dec_page_count(sbi, type) at line 1492
can bring the F2FS_WB_CP_DATA counter to zero, unblocking
f2fs_wait_on_all_pages() in f2fs_put_super() on a concurrent unmount
CPU. The unmount path then proceeds to call
f2fs_destroy_page_array_cache(sbi) and kfree(sbi). Meanwhile, the bio
completion callback is still executing: when it reaches
page_array_free(sbi, ...), it dereferences sbi->page_array_slab_size
and sbi->page_array_slab within the now-freed f2fs_sb_info structure.

This is the same class of bug as CVE-2026-23234 (which fixed the
equivalent race in f2fs_write_end_io() in data.c), but in the
compressed writeback completion path that was not covered by that fix.

Fix this by caching sbi->page_array_slab and sbi->page_array_slab_size
into local variables at function entry, before dec_page_count(). At
function entry, sbi is guaranteed valid because the F2FS_WB_CP_DATA
counter is still nonzero (this invocation has not yet decremented it),
preventing the unmount path from proceeding past
f2fs_wait_on_all_pages(). The cached values are then used in place of
the post-decrement sbi dereference.

Fixes: 4c8ff7095bef ("f2fs: support data compression")
Cc: stable@vger.kernel.org
Signed-off-by: George Saad <geoo115@gmail.com>
---
Changes in v3:
- Add Cc: stable@vger.kernel.org for backport to affected stable kernels

Changes in v2:
- Fix Fixes: tag commit hash (4c8ff7095bef, verified in Linus's tree)

 fs/f2fs/compress.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index 7b68bf229..c3d837df3 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -1479,11 +1479,20 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
 {
 	struct page *page = &folio->page;
 	struct f2fs_sb_info *sbi = bio->bi_private;
+	struct kmem_cache *pa_slab = sbi->page_array_slab;
+	unsigned int pa_slab_size = sbi->page_array_slab_size;
 	struct compress_io_ctx *cic = folio->private;
 	enum count_type type = WB_DATA_TYPE(folio,
 				f2fs_is_compressed_page(folio));
 	int i;
 
+	/*
+	 * Cache sbi fields before dec_page_count(), which may unblock
+	 * f2fs_wait_on_all_pages() in the unmount path, allowing
+	 * f2fs_put_super() to free sbi.  At this point sbi is still
+	 * valid because the F2FS_WB_CP_DATA counter is nonzero.
+	 */
+
 	if (unlikely(bio->bi_status != BLK_STS_OK))
 		mapping_set_error(cic->inode->i_mapping, -EIO);
 
@@ -1500,7 +1509,10 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
 		end_page_writeback(cic->rpages[i]);
 	}
 
-	page_array_free(sbi, cic->rpages, cic->nr_rpages);
+	if (likely(sizeof(struct page *) * cic->nr_rpages <= pa_slab_size))
+		kmem_cache_free(pa_slab, cic->rpages);
+	else
+		kfree(cic->rpages);
 	kmem_cache_free(cic_entry_slab, cic);
 }
 
-- 
2.53.0



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

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [f2fs-dev] [PATCH v3] f2fs: fix use-after-free of sbi in f2fs_compress_write_end_io()
  2026-03-23 10:44   ` [f2fs-dev] [PATCH v3] " George Saad
@ 2026-03-23 11:06     ` Chao Yu via Linux-f2fs-devel
  0 siblings, 0 replies; 11+ messages in thread
From: Chao Yu via Linux-f2fs-devel @ 2026-03-23 11:06 UTC (permalink / raw)
  To: George Saad, Greg KH; +Cc: Jaegeuk Kim, stable, linux-f2fs-devel

On 3/23/26 18:44, George Saad wrote:
> In f2fs_compress_write_end_io(), dec_page_count(sbi, type) at line 1492
> can bring the F2FS_WB_CP_DATA counter to zero, unblocking
> f2fs_wait_on_all_pages() in f2fs_put_super() on a concurrent unmount
> CPU. The unmount path then proceeds to call
> f2fs_destroy_page_array_cache(sbi) and kfree(sbi). Meanwhile, the bio
> completion callback is still executing: when it reaches
> page_array_free(sbi, ...), it dereferences sbi->page_array_slab_size
> and sbi->page_array_slab within the now-freed f2fs_sb_info structure.
> 
> This is the same class of bug as CVE-2026-23234 (which fixed the
> equivalent race in f2fs_write_end_io() in data.c), but in the
> compressed writeback completion path that was not covered by that fix.
> 
> Fix this by caching sbi->page_array_slab and sbi->page_array_slab_size
> into local variables at function entry, before dec_page_count(). At
> function entry, sbi is guaranteed valid because the F2FS_WB_CP_DATA
> counter is still nonzero (this invocation has not yet decremented it),
> preventing the unmount path from proceeding past
> f2fs_wait_on_all_pages(). The cached values are then used in place of
> the post-decrement sbi dereference.
> 
> Fixes: 4c8ff7095bef ("f2fs: support data compression")
> Cc: stable@vger.kernel.org
> Signed-off-by: George Saad <geoo115@gmail.com>
> ---
> Changes in v3:
> - Add Cc: stable@vger.kernel.org for backport to affected stable kernels
> 
> Changes in v2:
> - Fix Fixes: tag commit hash (4c8ff7095bef, verified in Linus's tree)
> 
>  fs/f2fs/compress.c | 14 +++++++++++++-
>  1 file changed, 13 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
> index 7b68bf229..c3d837df3 100644
> --- a/fs/f2fs/compress.c
> +++ b/fs/f2fs/compress.c
> @@ -1479,11 +1479,20 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
>  {
>  	struct page *page = &folio->page;
>  	struct f2fs_sb_info *sbi = bio->bi_private;
> +	struct kmem_cache *pa_slab = sbi->page_array_slab;
> +	unsigned int pa_slab_size = sbi->page_array_slab_size;
>  	struct compress_io_ctx *cic = folio->private;
>  	enum count_type type = WB_DATA_TYPE(folio,
>  				f2fs_is_compressed_page(folio));
>  	int i;
>  
> +	/*
> +	 * Cache sbi fields before dec_page_count(), which may unblock
> +	 * f2fs_wait_on_all_pages() in the unmount path, allowing
> +	 * f2fs_put_super() to free sbi.  At this point sbi is still
> +	 * valid because the F2FS_WB_CP_DATA counter is nonzero.
> +	 */
> +
>  	if (unlikely(bio->bi_status != BLK_STS_OK))
>  		mapping_set_error(cic->inode->i_mapping, -EIO);
>  
> @@ -1500,7 +1509,10 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
>  		end_page_writeback(cic->rpages[i]);
>  	}
>  
> -	page_array_free(sbi, cic->rpages, cic->nr_rpages);
> +	if (likely(sizeof(struct page *) * cic->nr_rpages <= pa_slab_size))
> +		kmem_cache_free(pa_slab, cic->rpages);

After sbi is freed, sbi->page_array_slab should be destroyed as well, so
pa_slab points to a freed memory, right?

Thanks,

> +	else
> +		kfree(cic->rpages);
>  	kmem_cache_free(cic_entry_slab, cic);
>  }
>  



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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [f2fs-dev] [PATCH v4] f2fs: fix use-after-free of sbi in f2fs_compress_write_end_io()
  2026-03-23  7:46 ` Greg KH
                     ` (2 preceding siblings ...)
  2026-03-23 10:44   ` [f2fs-dev] [PATCH v3] " George Saad
@ 2026-03-23 11:21   ` George Saad
  2026-03-23 11:30     ` Chao Yu via Linux-f2fs-devel
  2026-03-24 17:32     ` patchwork-bot+f2fs--- via Linux-f2fs-devel
  3 siblings, 2 replies; 11+ messages in thread
From: George Saad @ 2026-03-23 11:21 UTC (permalink / raw)
  To: Greg KH; +Cc: Jaegeuk Kim, George Saad, stable, linux-f2fs-devel

In f2fs_compress_write_end_io(), dec_page_count(sbi, type) can bring
the F2FS_WB_CP_DATA counter to zero, unblocking
f2fs_wait_on_all_pages() in f2fs_put_super() on a concurrent unmount
CPU. The unmount path then proceeds to call
f2fs_destroy_page_array_cache(sbi), which destroys
sbi->page_array_slab via kmem_cache_destroy(), and eventually
kfree(sbi). Meanwhile, the bio completion callback is still executing:
when it reaches page_array_free(sbi, ...), it dereferences
sbi->page_array_slab — a destroyed slab cache — to call
kmem_cache_free(), causing a use-after-free.

This is the same class of bug as CVE-2026-23234 (which fixed the
equivalent race in f2fs_write_end_io() in data.c), but in the
compressed writeback completion path that was not covered by that fix.

Fix this by moving dec_page_count() to after page_array_free(), so
that all sbi accesses complete before the counter decrement that can
unblock unmount. For non-last folios (where atomic_dec_return on
cic->pending_pages is nonzero), dec_page_count is called immediately
before returning — page_array_free is not reached on this path, so
there is no post-decrement sbi access. For the last folio,
page_array_free runs while the F2FS_WB_CP_DATA counter is still
nonzero (this folio has not yet decremented it), keeping sbi alive,
and dec_page_count runs as the final operation.

Fixes: 4c8ff7095bef ("f2fs: support data compression")
Cc: stable@vger.kernel.org
Signed-off-by: George Saad <geoo115@gmail.com>
---
Changes in v4:
- Rewrite fix: instead of caching sbi->page_array_slab (which is
  destroyed by kmem_cache_destroy before kfree(sbi)), move
  dec_page_count() to after page_array_free() so all sbi accesses
  complete before the counter decrement can unblock unmount
  (Chao Yu)

Changes in v3:
- Add Cc: stable@vger.kernel.org for backport to affected stable kernels

Changes in v2:
- Fix Fixes: tag commit hash (4c8ff7095bef, verified in Linus's tree)

 fs/f2fs/compress.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index 7b68bf229..d9d105efa 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -1489,10 +1489,10 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
 
 	f2fs_compress_free_page(page);
 
-	dec_page_count(sbi, type);
-
-	if (atomic_dec_return(&cic->pending_pages))
+	if (atomic_dec_return(&cic->pending_pages)) {
+		dec_page_count(sbi, type);
 		return;
+	}
 
 	for (i = 0; i < cic->nr_rpages; i++) {
 		WARN_ON(!cic->rpages[i]);
@@ -1502,6 +1502,14 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
 
 	page_array_free(sbi, cic->rpages, cic->nr_rpages);
 	kmem_cache_free(cic_entry_slab, cic);
+
+	/*
+	 * Make sure dec_page_count() is the last access to sbi.
+	 * Once it drops the F2FS_WB_CP_DATA counter to zero, the
+	 * unmount thread can proceed to destroy sbi and
+	 * sbi->page_array_slab.
+	 */
+	dec_page_count(sbi, type);
 }
 
 static int f2fs_write_raw_pages(struct compress_ctx *cc,
-- 
2.53.0



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

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [f2fs-dev] [PATCH v4] f2fs: fix use-after-free of sbi in f2fs_compress_write_end_io()
  2026-03-23 11:21   ` [f2fs-dev] [PATCH v4] " George Saad
@ 2026-03-23 11:30     ` Chao Yu via Linux-f2fs-devel
  2026-03-24 17:32     ` patchwork-bot+f2fs--- via Linux-f2fs-devel
  1 sibling, 0 replies; 11+ messages in thread
From: Chao Yu via Linux-f2fs-devel @ 2026-03-23 11:30 UTC (permalink / raw)
  To: George Saad, Greg KH; +Cc: Jaegeuk Kim, stable, linux-f2fs-devel

On 3/23/26 19:21, George Saad wrote:
> In f2fs_compress_write_end_io(), dec_page_count(sbi, type) can bring
> the F2FS_WB_CP_DATA counter to zero, unblocking
> f2fs_wait_on_all_pages() in f2fs_put_super() on a concurrent unmount
> CPU. The unmount path then proceeds to call
> f2fs_destroy_page_array_cache(sbi), which destroys
> sbi->page_array_slab via kmem_cache_destroy(), and eventually
> kfree(sbi). Meanwhile, the bio completion callback is still executing:
> when it reaches page_array_free(sbi, ...), it dereferences
> sbi->page_array_slab — a destroyed slab cache — to call
> kmem_cache_free(), causing a use-after-free.
> 
> This is the same class of bug as CVE-2026-23234 (which fixed the
> equivalent race in f2fs_write_end_io() in data.c), but in the
> compressed writeback completion path that was not covered by that fix.
> 
> Fix this by moving dec_page_count() to after page_array_free(), so
> that all sbi accesses complete before the counter decrement that can
> unblock unmount. For non-last folios (where atomic_dec_return on
> cic->pending_pages is nonzero), dec_page_count is called immediately
> before returning — page_array_free is not reached on this path, so
> there is no post-decrement sbi access. For the last folio,
> page_array_free runs while the F2FS_WB_CP_DATA counter is still
> nonzero (this folio has not yet decremented it), keeping sbi alive,
> and dec_page_count runs as the final operation.
> 
> Fixes: 4c8ff7095bef ("f2fs: support data compression")
> Cc: stable@vger.kernel.org
> Signed-off-by: George Saad <geoo115@gmail.com>

Reviewed-by: Chao Yu <chao@kernel.org>

Thanks,


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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [f2fs-dev] [PATCH v4] f2fs: fix use-after-free of sbi in f2fs_compress_write_end_io()
  2026-03-23 11:21   ` [f2fs-dev] [PATCH v4] " George Saad
  2026-03-23 11:30     ` Chao Yu via Linux-f2fs-devel
@ 2026-03-24 17:32     ` patchwork-bot+f2fs--- via Linux-f2fs-devel
  1 sibling, 0 replies; 11+ messages in thread
From: patchwork-bot+f2fs--- via Linux-f2fs-devel @ 2026-03-24 17:32 UTC (permalink / raw)
  To: George Saad; +Cc: gregkh, linux-f2fs-devel, stable, jaegeuk

Hello:

This patch was applied to jaegeuk/f2fs.git (dev)
by Jaegeuk Kim <jaegeuk@kernel.org>:

On Mon, 23 Mar 2026 11:21:23 +0000 you wrote:
> In f2fs_compress_write_end_io(), dec_page_count(sbi, type) can bring
> the F2FS_WB_CP_DATA counter to zero, unblocking
> f2fs_wait_on_all_pages() in f2fs_put_super() on a concurrent unmount
> CPU. The unmount path then proceeds to call
> f2fs_destroy_page_array_cache(sbi), which destroys
> sbi->page_array_slab via kmem_cache_destroy(), and eventually
> kfree(sbi). Meanwhile, the bio completion callback is still executing:
> when it reaches page_array_free(sbi, ...), it dereferences
> sbi->page_array_slab — a destroyed slab cache — to call
> kmem_cache_free(), causing a use-after-free.
> 
> [...]

Here is the summary with links:
  - [f2fs-dev,v4] f2fs: fix use-after-free of sbi in f2fs_compress_write_end_io()
    https://git.kernel.org/jaegeuk/f2fs/c/39d4ee19c1e7

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html




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

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-03-24 17:32 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-22 21:31 [f2fs-dev] [PATCH] f2fs: fix use-after-free in f2fs_compress_write_end_io() G S
2026-03-23  7:46 ` Greg KH
2026-03-23  9:03   ` [f2fs-dev] [PATCH] f2fs: fix use-after-free of sbi " George Saad
2026-03-23  9:32     ` Greg KH
2026-03-23  9:38   ` [f2fs-dev] [PATCH v2] " George Saad
2026-03-23 10:38     ` Greg KH
2026-03-23 10:44   ` [f2fs-dev] [PATCH v3] " George Saad
2026-03-23 11:06     ` Chao Yu via Linux-f2fs-devel
2026-03-23 11:21   ` [f2fs-dev] [PATCH v4] " George Saad
2026-03-23 11:30     ` Chao Yu via Linux-f2fs-devel
2026-03-24 17:32     ` patchwork-bot+f2fs--- via Linux-f2fs-devel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox