FAILED: patch "[PATCH] mm/huge_memory: fix a folio_split() race condition with" failed to apply to 6.18-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.18-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.18.y
git checkout FETCH_HEAD
git cherry-pick -x 577a1f495fd78d8fb61b67ac3d3b595b01f6fcb0
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026031741-handmade-lilly-566b@gregkh' --subject-prefix 'PATCH 6.18.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 577a1f495fd78d8fb61b67ac3d3b595b01f6fcb0 Mon Sep 17 00:00:00 2001
From: Zi Yan <[email protected]>
Date: Mon, 2 Mar 2026 15:31:59 -0500
Subject: [PATCH] mm/huge_memory: fix a folio_split() race condition with
folio_try_get()
During a pagecache folio split, the values in the related xarray should not be changed from the original folio at xarray split time until all after-split folios are well formed and stored in the xarray. Current use of xas_try_split() in __split_unmapped_folio() lets some after-split folios show up at wrong indices in the xarray. When these misplaced after-split folios are unfrozen, before correct folios are stored via __xa_store(), and grabbed by folio_try_get(), they are returned to userspace at wrong file indices, causing data corruption. More detailed explanation is at the bottom.
The reproducer is at: https://github.com/dfinity/thp-madv-remove-test
It
1. creates a memfd,
2. forks,
3. in the child process, maps the file with large folios (via shmem code path) and reads the mapped file continuously with 16 threads,
4. in the parent process, uses madvise(MADV_REMOVE) to punch poles in the large folio.
Data corruption can be observed without the fix. Basically, data from a wrong page->index is returned.
Fix it by using the original folio in xas_try_split() calls, so that folio_try_get() can get the right after-split folios after the original folio is unfrozen.
Uniform split, split_huge_page*(), is not affected, since it uses xas_split_alloc() and xas_split() only once and stores the original folio in the xarray. Change xas_split() used in uniform split branch to use the original folio to avoid confusion.
Fixes below points to the commit introduces the code, but folio_split() is used in a later commit 7460b470a131f ("mm/truncate: use folio_split() in truncate operation").
More details:
For example, a folio f is split non-uniformly into f, f2, f3, f4 like below:
+----------------+---------+----+----+ | f | f2 | f3 | f4 | +----------------+---------+----+----+ but the xarray would look like below after __split_unmapped_folio() is done: +----------------+---------+----+----+ | f | f2 | f3 | f3 | +----------------+---------+----+----+
After __split_unmapped_folio(), the code changes the xarray and unfreezes after-split folios:
1. unfreezes f2, __xa_store(f2)
2. unfreezes f3, __xa_store(f3)
3. unfreezes f4, __xa_store(f4), which overwrites the second f3 to f4.
4. unfreezes f.
Meanwhile, a parallel filemap_get_entry() can read the second f3 from the xarray and use folio_try_get() on it at step 2 when f3 is unfrozen. Then, f3 is wrongly returned to user.
After the fix, the xarray looks like below after __split_unmapped_folio():
+----------------+---------+----+----+ | f | f | f | f | +----------------+---------+----+----+
so that the race window no longer exists.
mm/huge_memory.c +9 -4
@@ -3631,6 +3631,7 @@ static int __split_unmapped_folio(struct folio *folio, int new_order,const bool is_anon =folio_test_anon (folio);int old_order =folio_order (folio);int start_order = split_type ==SPLIT_TYPE_UNIFORM ? new_order : old_order -1 ; +struct folio *old_folio = folio;int split_order;/*
@@ -3651,12 +3652,16 @@ static int __split_unmapped_folio(struct folio *folio, int new_order,* uniform split has xas_split_alloc() called before * irq is disabled to allocate enough memory, whereas * non-uniform split can handle ENOMEM. +* Use the to-be-split folio, so that a parallel +* folio_try_get() waits on it until xarray is updated +* with after-split folios and the original one is +* unfrozen. */ -if (split_type ==SPLIT_TYPE_UNIFORM ) -xas_split (xas, folio, old_order); -else { +if (split_type ==SPLIT_TYPE_UNIFORM ) { +xas_split (xas, old_folio, old_order); + }else {xas_set_order (xas, folio->index, split_order); -xas_try_split (xas, folio, old_order); +xas_try_split (xas, old_folio, old_order);if (xas_error (xas))return xas_error (xas); }
Zi Yan
reviewerPATCH 1/1 mm/huge_memory: fix a folio_split() race condition with folio_try_get()
During a pagecache folio split, the values in the related xarray should not be changed from the original folio at xarray split time until all after-split folios are well formed and stored in the xarray. Current use of xas_try_split() in __split_unmapped_folio() lets some after-split folios show up at wrong indices in the xarray. When these misplaced after-split folios are unfrozen, before correct folios are stored via __xa_store(), and grabbed by folio_try_get(), they are returned to userspace at wrong file indices, causing data corruption. More detailed explanation is at the bottom.
The reproducer is at: https://github.com/dfinity/thp-madv-remove-test
It
1. creates a memfd,
2. forks,
3. in the child process, maps the file with large folios (via shmem code path) and reads the mapped file continuously with 16 threads,
4. in the parent process, uses madvise(MADV_REMOVE) to punch poles in the large folio.
Data corruption can be observed without the fix. Basically, data from a wrong page->index is returned.
Fix it by using the original folio in xas_try_split() calls, so that folio_try_get() can get the right after-split folios after the original folio is unfrozen.
Uniform split, split_huge_page*(), is not affected, since it uses xas_split_alloc() and xas_split() only once and stores the original folio in the xarray. Change xas_split() used in uniform split branch to use the original folio to avoid confusion.
Fixes below points to the commit introduces the code, but folio_split() is used in a later commit 7460b470a131f ("mm/truncate: use folio_split() in truncate operation").
More details:
For example, a folio f is split non-uniformly into f, f2, f3, f4 like below:
+----------------+---------+----+----+ | f | f2 | f3 | f4 | +----------------+---------+----+----+ but the xarray would look like below after __split_unmapped_folio() is done: +----------------+---------+----+----+ | f | f2 | f3 | f3 | +----------------+---------+----+----+
After __split_unmapped_folio(), the code changes the xarray and unfreezes after-split folios:
1. unfreezes f2, __xa_store(f2)
2. unfreezes f3, __xa_store(f3)
3. unfreezes f4, __xa_store(f4), which overwrites the second f3 to f4.
4. unfreezes f.
Meanwhile, a parallel filemap_get_entry() can read the second f3 from the xarray and use folio_try_get() on it at step 2 when f3 is unfrozen. Then, f3 is wrongly returned to user.
After the fix, the xarray looks like below after __split_unmapped_folio():
+----------------+---------+----+----+ | f | f | f | f | +----------------+---------+----+----+
so that the race window no longer exists.
--- mm/huge_memory.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
mm/huge_memory.c +7 -2
@@ -3437,6 +3437,7 @@ static int __split_unmapped_folio(struct folio *folio, int new_order, {int order =folio_order (folio);int start_order = uniform_split ? new_order : order -1 ; +struct folio *old_folio = folio;bool stop_split = false;struct folio *next;int split_order;
@@ -3467,12 +3468,16 @@ static int __split_unmapped_folio(struct folio *folio, int new_order,* uniform split has xas_split_alloc() called before * irq is disabled to allocate enough memory, whereas * non-uniform split can handle ENOMEM. +* Use the to-be-split folio, so that a parallel +* folio_try_get() waits on it until xarray is updated +* with after-split folios and the original one is +* unfrozen. */ if (uniform_split) -xas_split (xas, folio, old_order); +xas_split (xas, old_folio, old_order);else {xas_set_order (xas, folio->index, split_order); -xas_try_split (xas, folio, old_order); +xas_try_split (xas, old_folio, old_order);if (xas_error (xas)) { ret =xas_error (xas); stop_split = true;
Signature · 1 line
2.51.0