* [PATCH v2 0/5] mm: fix inode UAF when splitting a file folio past EOF
@ 2026-07-14 12:23 Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split Kiryl Shutsemau
` (4 more replies)
0 siblings, 5 replies; 13+ messages in thread
From: Kiryl Shutsemau @ 2026-07-14 12:23 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Miaohe Lin,
Naoya Horiguchi
Cc: Zi Yan, Baolin Wang, Liam R . Howlett, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Usama Arif, Hao Zhang,
Hao Zhang, linux-mm, linux-kernel, Kiryl Shutsemau (Meta)
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
This fixes a use-after-free of the inode in __folio_split(), reported by
Hao Zhang, and cleans up the split helpers the fix leaves unused.
The bug: __folio_split() keeps dereferencing folio->mapping after the
split (shmem_uncharge(), i_mmap_unlock_read()), relying on the locked
@lock_at folio staying in the page cache to hold off inode eviction.
memory_failure() splits at a poisoned *tail*, and when that tail is
beyond EOF the split drops it from the page cache yet still returns it
locked -- the inode pin is gone, and a racing final iput() can evict and
free the inode mid-split.
v1 [1] pinned the inode with igrab()/iput() inside __folio_split().
Sashiko pointed out that the iput() under the folio lock can
self-deadlock: if it drops the last reference, eviction blocks on the
very folio the caller still holds locked. Thanks!
v2 drops that approach for two smaller changes:
- memory_failure() is the only caller that passed a tail as the split
anchor. Patch 1 makes it lock and split at the head instead. The head
is piece 0, which the beyond-EOF drop loop never removes, so it always
stays in the page cache and keeps the inode pinned. This alone fixes
the UAF.
- Patch 2 makes the requirement explicit in __folio_split(): refuse the
split with -EBUSY when the anchor is beyond EOF, so no future caller
can reintroduce it. No refcounting, no lock reordering.
Both are tagged for stable; patch 1 comes first so patch 2's guard never
changes memory_failure() behaviour.
Patches 3-5 are cleanups (not for stable). Patch 1 leaves
split_huge_page_to_order() with no callers, and the audit found
can_split_folio() (dead) and split_folio_to_list_to_order() (only ever
called with a NULL list) alongside it. Remove/fold them.
Only compile- and boot-tested: I could confirm memory_failure() enters
__folio_split() with a tail anchor (the pattern patch 1 removes) and that
the fixed kernel is stable, but not land the exact UAF -- the race window
is narrow enough that my instrumentation could not open it reliably.
[1] https://lore.kernel.org/all/20260713170915.239819-1-kirill@shutemov.name
Kiryl Shutsemau (Meta) (5):
mm/memory-failure: keep the folio, not the poisoned subpage, locked
across split
mm/huge_memory: refuse to split a file folio when the anchor is beyond
EOF
mm/huge_memory: remove unused split_huge_page_to_order()
mm/huge_memory: remove unused can_split_folio()
mm/huge_memory: fold split_folio_to_list_to_order() into
split_folio_to_order()
include/linux/huge_mm.h | 22 +---------------------
mm/huge_memory.c | 14 ++++++++++++++
mm/memory-failure.c | 13 ++++++++++---
3 files changed, 25 insertions(+), 24 deletions(-)
base-commit: 0e35b9b6ec0ffcc5e23cbdec09f5c622ad532b53
--
2.54.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split
2026-07-14 12:23 [PATCH v2 0/5] mm: fix inode UAF when splitting a file folio past EOF Kiryl Shutsemau
@ 2026-07-14 12:23 ` Kiryl Shutsemau
2026-07-14 13:01 ` David Hildenbrand (Arm)
2026-07-14 13:05 ` Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 2/5] mm/huge_memory: refuse to split a file folio when the anchor is beyond EOF Kiryl Shutsemau
` (3 subsequent siblings)
4 siblings, 2 replies; 13+ messages in thread
From: Kiryl Shutsemau @ 2026-07-14 12:23 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Miaohe Lin,
Naoya Horiguchi
Cc: Zi Yan, Baolin Wang, Liam R . Howlett, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Usama Arif, Hao Zhang,
Hao Zhang, linux-mm, linux-kernel, Kiryl Shutsemau (Meta), stable
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
try_to_split_thp_page() locked the poisoned page and passed it to
split_huge_page_to_order(), which returns that very page locked to the
caller. For a tail page that means __folio_split() runs with @lock_at
pointing into the middle of the folio.
__folio_split() dereferences the mapping after the split completes
(shmem_uncharge(), i_mmap_unlock_read()). The only thing keeping the
inode alive across that is the locked @lock_at folio: while it stays in
the page cache, eviction cannot complete.
But a tail @lock_at can lie beyond EOF -- e.g. part of a shmem THP that
reaches past i_size while the file is being truncated. The split then
drops it from the page cache yet still returns it locked, so the pin is
gone and a racing final iput() can evict and RCU-free the inode while
__folio_split() is still running:
BUG: KASAN: slab-use-after-free in __up_read+0x634/0x790
i_mmap_unlock_read include/linux/fs.h:537 [inline]
__folio_split+0x732/0x1640 mm/huge_memory.c:4100
try_to_split_thp_page+0xab/0x390 mm/memory-failure.c:1675
memory_failure+0x1394/0x26e0 mm/memory-failure.c:2470
Freed by task 4601:
shmem_free_in_core_inode+0x54/0xb0 mm/shmem.c:5177
evict+0x57f/0xac0 fs/inode.c:870
Split the folio as a folio, via split_folio_to_order(), so the head is
the anchor left locked. The head is piece 0, which the beyond-EOF drop
loop never removes (it starts at folio_next(folio)), so the split always
leaves it in the page cache and the inode stays pinned for the whole of
__folio_split(). memory_failure() and soft offline re-lock the poisoned
subpage's folio themselves after the split, so they do not depend on it
being returned locked.
Reported-by: Hao Zhang <zhanghao1@kylinos.cn>
Closes: https://lore.kernel.org/linux-mm/20260710071344.GA106129@zh-pc
Fixes: baa355fd3314 ("thp: file pages support for split_huge_page()")
Cc: <stable@vger.kernel.org>
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
mm/memory-failure.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 51508a55c405..68d42cbed458 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1657,11 +1657,18 @@ static int identify_page_state(unsigned long pfn, struct page *p,
static int try_to_split_thp_page(struct page *page, unsigned int new_order,
bool release)
{
+ struct folio *folio = page_folio(page);
int ret;
- lock_page(page);
- ret = split_huge_page_to_order(page, new_order);
- unlock_page(page);
+ /*
+ * Lock and split at the head, not the poisoned subpage: __folio_split()
+ * keeps the anchor folio locked and needs it to stay in the page cache
+ * to pin the inode. A tail beyond EOF would be dropped yet returned
+ * locked, losing that pin. The caller re-locks @page afterwards.
+ */
+ folio_lock(folio);
+ ret = split_folio_to_order(folio, new_order);
+ folio_unlock(folio);
if (ret && release)
put_page(page);
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 2/5] mm/huge_memory: refuse to split a file folio when the anchor is beyond EOF
2026-07-14 12:23 [PATCH v2 0/5] mm: fix inode UAF when splitting a file folio past EOF Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split Kiryl Shutsemau
@ 2026-07-14 12:23 ` Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 3/5] mm/huge_memory: remove unused split_huge_page_to_order() Kiryl Shutsemau
` (2 subsequent siblings)
4 siblings, 0 replies; 13+ messages in thread
From: Kiryl Shutsemau @ 2026-07-14 12:23 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Miaohe Lin,
Naoya Horiguchi
Cc: Zi Yan, Baolin Wang, Liam R . Howlett, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Usama Arif, Hao Zhang,
Hao Zhang, linux-mm, linux-kernel, Kiryl Shutsemau (Meta), stable
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
__folio_split() dereferences the mapping after the split completes:
shmem_uncharge(mapping->host) for folios dropped beyond EOF and
i_mmap_unlock_read(mapping) on the way out. Nothing holds an inode
reference for that duration; the split relies on the caller's locked
@lock_at folio, while it is locked and present in the page cache, to keep
the inode alive through eviction's truncate_inode_pages_final().
If @lock_at lies beyond EOF, __folio_freeze_and_split_unmapped() removes
it from the page cache while keeping it locked for the caller. That drops
the pin and lets a concurrent final iput() evict and free the inode under
the still-running split. On the anon side __folio_split() already pins
its anchor explicitly (folio_get_anon_vma()); the file side's anchor was
always the locked in-cache folio, just never enforced.
The only in-tree caller that passed a beyond-EOF @lock_at was
memory_failure(), fixed in the previous patch to anchor on the head. Make
the requirement explicit so it cannot be reintroduced: refuse the split
with -EBUSY when @lock_at is at or beyond the sampled EOF. Such a folio
is racing truncation, so there is nothing useful to split; -EBUSY is
already handled by every caller.
The check uses the same @end sampled under the folio lock that the drop
loop uses, so it does not race the trimming it guards against.
Fixes: baa355fd3314 ("thp: file pages support for split_huge_page()")
Cc: <stable@vger.kernel.org>
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
mm/huge_memory.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 2bccb0a53a0a..0e3ca7178d8c 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4065,6 +4065,20 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE);
if (shmem_mapping(mapping))
end = shmem_fallocend(mapping->host, end);
+
+ /*
+ * @lock_at is returned locked to the caller, and while it is
+ * locked and present in the page cache it is what keeps the
+ * inode alive: the mapping is still dereferenced after the split
+ * (shmem_uncharge(), i_mmap_unlock_read()). If it lies beyond
+ * EOF the split would drop it from the page cache while handing
+ * it back locked, removing that pin. Such a folio is racing
+ * truncation and there is nothing useful to split; bail out.
+ */
+ if (folio->index + folio_page_idx(folio, lock_at) >= end) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
}
/*
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 3/5] mm/huge_memory: remove unused split_huge_page_to_order()
2026-07-14 12:23 [PATCH v2 0/5] mm: fix inode UAF when splitting a file folio past EOF Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 2/5] mm/huge_memory: refuse to split a file folio when the anchor is beyond EOF Kiryl Shutsemau
@ 2026-07-14 12:23 ` Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 4/5] mm/huge_memory: remove unused can_split_folio() Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 5/5] mm/huge_memory: fold split_folio_to_list_to_order() into split_folio_to_order() Kiryl Shutsemau
4 siblings, 0 replies; 13+ messages in thread
From: Kiryl Shutsemau @ 2026-07-14 12:23 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Miaohe Lin,
Naoya Horiguchi
Cc: Zi Yan, Baolin Wang, Liam R . Howlett, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Usama Arif, Hao Zhang,
Hao Zhang, linux-mm, linux-kernel, Kiryl Shutsemau (Meta)
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
The last caller, memory_failure()'s try_to_split_thp_page(), was
converted to the folio-native split_folio_to_order() in the previous
change. split_huge_page_to_order() now has no users; the folio helpers
(split_folio_to_order(), split_folio_to_list_to_order()) cover uniform
splits and split_huge_page() covers the order-0 case. Remove it.
No functional change.
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
include/linux/huge_mm.h | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index ad20f7f8c179..77483c613f84 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -414,10 +414,6 @@ static inline int split_huge_page_to_list_to_order(struct page *page, struct lis
{
return __split_huge_page_to_list_to_order(page, list, new_order);
}
-static inline int split_huge_page_to_order(struct page *page, unsigned int new_order)
-{
- return split_huge_page_to_list_to_order(page, NULL, new_order);
-}
static inline int split_huge_page(struct page *page)
{
@@ -633,11 +629,6 @@ split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
VM_WARN_ON_ONCE_PAGE(1, page);
return -EINVAL;
}
-static inline int split_huge_page_to_order(struct page *page, unsigned int new_order)
-{
- VM_WARN_ON_ONCE_PAGE(1, page);
- return -EINVAL;
-}
static inline int split_huge_page(struct page *page)
{
VM_WARN_ON_ONCE_PAGE(1, page);
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 4/5] mm/huge_memory: remove unused can_split_folio()
2026-07-14 12:23 [PATCH v2 0/5] mm: fix inode UAF when splitting a file folio past EOF Kiryl Shutsemau
` (2 preceding siblings ...)
2026-07-14 12:23 ` [PATCH v2 3/5] mm/huge_memory: remove unused split_huge_page_to_order() Kiryl Shutsemau
@ 2026-07-14 12:23 ` Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 5/5] mm/huge_memory: fold split_folio_to_list_to_order() into split_folio_to_order() Kiryl Shutsemau
4 siblings, 0 replies; 13+ messages in thread
From: Kiryl Shutsemau @ 2026-07-14 12:23 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Miaohe Lin,
Naoya Horiguchi
Cc: Zi Yan, Baolin Wang, Liam R . Howlett, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Usama Arif, Hao Zhang,
Hao Zhang, linux-mm, linux-kernel, Kiryl Shutsemau (Meta)
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
can_split_folio() has no callers and no definition under
CONFIG_TRANSPARENT_HUGEPAGE; only the !THP stub remains, left behind by an
earlier cleanup. Remove it.
No functional change.
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
include/linux/huge_mm.h | 5 -----
1 file changed, 5 deletions(-)
diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index 77483c613f84..a38db095b5bd 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -617,11 +617,6 @@ thp_get_unmapped_area_vmflags(struct file *filp, unsigned long addr,
return 0;
}
-static inline bool
-can_split_folio(struct folio *folio, int caller_pins, int *pextra_pins)
-{
- return false;
-}
static inline int
split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
unsigned int new_order)
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 5/5] mm/huge_memory: fold split_folio_to_list_to_order() into split_folio_to_order()
2026-07-14 12:23 [PATCH v2 0/5] mm: fix inode UAF when splitting a file folio past EOF Kiryl Shutsemau
` (3 preceding siblings ...)
2026-07-14 12:23 ` [PATCH v2 4/5] mm/huge_memory: remove unused can_split_folio() Kiryl Shutsemau
@ 2026-07-14 12:23 ` Kiryl Shutsemau
4 siblings, 0 replies; 13+ messages in thread
From: Kiryl Shutsemau @ 2026-07-14 12:23 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Miaohe Lin,
Naoya Horiguchi
Cc: Zi Yan, Baolin Wang, Liam R . Howlett, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Usama Arif, Hao Zhang,
Hao Zhang, linux-mm, linux-kernel, Kiryl Shutsemau (Meta)
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
split_folio_to_list_to_order() had no direct callers; its only user was
split_folio_to_order(), which always passed a NULL list. Fold it into
split_folio_to_order() and call split_huge_page_to_list_to_order()
directly.
No functional change.
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
include/linux/huge_mm.h | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index a38db095b5bd..8a10a886e62a 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -783,15 +783,9 @@ static inline bool is_pmd_order(unsigned int order)
return order == HPAGE_PMD_ORDER;
}
-static inline int split_folio_to_list_to_order(struct folio *folio,
- struct list_head *list, int new_order)
-{
- return split_huge_page_to_list_to_order(&folio->page, list, new_order);
-}
-
static inline int split_folio_to_order(struct folio *folio, int new_order)
{
- return split_folio_to_list_to_order(folio, NULL, new_order);
+ return split_huge_page_to_list_to_order(&folio->page, NULL, new_order);
}
/**
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split
2026-07-14 12:23 ` [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split Kiryl Shutsemau
@ 2026-07-14 13:01 ` David Hildenbrand (Arm)
2026-07-14 14:53 ` Kiryl Shutsemau
2026-07-14 13:05 ` Kiryl Shutsemau
1 sibling, 1 reply; 13+ messages in thread
From: David Hildenbrand (Arm) @ 2026-07-14 13:01 UTC (permalink / raw)
To: Kiryl Shutsemau, Andrew Morton, Lorenzo Stoakes, Miaohe Lin,
Naoya Horiguchi
Cc: Zi Yan, Baolin Wang, Liam R . Howlett, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Usama Arif, Hao Zhang,
Hao Zhang, linux-mm, linux-kernel, Kiryl Shutsemau (Meta), stable
On 7/14/26 14:23, Kiryl Shutsemau wrote:
> From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
>
> try_to_split_thp_page() locked the poisoned page and passed it to
> split_huge_page_to_order(), which returns that very page locked to the
> caller. For a tail page that means __folio_split() runs with @lock_at
> pointing into the middle of the folio.
>
> __folio_split() dereferences the mapping after the split completes
> (shmem_uncharge(), i_mmap_unlock_read()). The only thing keeping the
> inode alive across that is the locked @lock_at folio: while it stays in
> the page cache, eviction cannot complete.
>
> But a tail @lock_at can lie beyond EOF -- e.g. part of a shmem THP that
> reaches past i_size while the file is being truncated. The split then
> drops it from the page cache yet still returns it locked, so the pin is
> gone and a racing final iput() can evict and RCU-free the inode while
> __folio_split() is still running:
>
> BUG: KASAN: slab-use-after-free in __up_read+0x634/0x790
> i_mmap_unlock_read include/linux/fs.h:537 [inline]
> __folio_split+0x732/0x1640 mm/huge_memory.c:4100
> try_to_split_thp_page+0xab/0x390 mm/memory-failure.c:1675
> memory_failure+0x1394/0x26e0 mm/memory-failure.c:2470
>
> Freed by task 4601:
> shmem_free_in_core_inode+0x54/0xb0 mm/shmem.c:5177
> evict+0x57f/0xac0 fs/inode.c:870
>
> Split the folio as a folio, via split_folio_to_order(), so the head is
> the anchor left locked. The head is piece 0, which the beyond-EOF drop
> loop never removes (it starts at folio_next(folio)), so the split always
> leaves it in the page cache and the inode stays pinned for the whole of
> __folio_split(). memory_failure() and soft offline re-lock the poisoned
> subpage's folio themselves after the split, so they do not depend on it
> being returned locked.
>
> Reported-by: Hao Zhang <zhanghao1@kylinos.cn>
> Closes: https://lore.kernel.org/linux-mm/20260710071344.GA106129@zh-pc
> Fixes: baa355fd3314 ("thp: file pages support for split_huge_page()")
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
> ---
> mm/memory-failure.c | 13 ++++++++++---
> 1 file changed, 10 insertions(+), 3 deletions(-)
>
> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index 51508a55c405..68d42cbed458 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -1657,11 +1657,18 @@ static int identify_page_state(unsigned long pfn, struct page *p,
> static int try_to_split_thp_page(struct page *page, unsigned int new_order,
> bool release)
> {
> + struct folio *folio = page_folio(page);
> int ret;
>
> - lock_page(page);
> - ret = split_huge_page_to_order(page, new_order);
> - unlock_page(page);
> + /*
> + * Lock and split at the head, not the poisoned subpage: __folio_split()
> + * keeps the anchor folio locked and needs it to stay in the page cache
> + * to pin the inode. A tail beyond EOF would be dropped yet returned
> + * locked, losing that pin. The caller re-locks @page afterwards.
> + */
> + folio_lock(folio);
> + ret = split_folio_to_order(folio, new_order);
> + folio_unlock(folio);
With a non-uniform split it would actually make a difference: we'd want to split
such that we the other folio pages minimal.
split_folio_to_order() always seems to end up in
__split_huge_page_to_list_to_order() where we do a SPLIT_TYPE_UNIFORM.
I recall discussing with Zi and Willy that in the future we'd want to convert
more places to do a non-uniform split.
So I'm afraid that would just re-introduce the problem then.
--
Cheers,
David
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split
2026-07-14 12:23 ` [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split Kiryl Shutsemau
2026-07-14 13:01 ` David Hildenbrand (Arm)
@ 2026-07-14 13:05 ` Kiryl Shutsemau
1 sibling, 0 replies; 13+ messages in thread
From: Kiryl Shutsemau @ 2026-07-14 13:05 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Miaohe Lin,
Naoya Horiguchi
Cc: Zi Yan, Baolin Wang, Liam R . Howlett, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Usama Arif, Hao Zhang,
Hao Zhang, linux-mm, linux-kernel, stable
On Tue, Jul 14, 2026 at 01:23:40PM +0100, Kiryl Shutsemau wrote:
> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index 51508a55c405..68d42cbed458 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -1657,11 +1657,18 @@ static int identify_page_state(unsigned long pfn, struct page *p,
> static int try_to_split_thp_page(struct page *page, unsigned int new_order,
> bool release)
> {
> + struct folio *folio = page_folio(page);
> int ret;
>
> - lock_page(page);
> - ret = split_huge_page_to_order(page, new_order);
> - unlock_page(page);
> + /*
> + * Lock and split at the head, not the poisoned subpage: __folio_split()
> + * keeps the anchor folio locked and needs it to stay in the page cache
> + * to pin the inode. A tail beyond EOF would be dropped yet returned
> + * locked, losing that pin. The caller re-locks @page afterwards.
> + */
> + folio_lock(folio);
> + ret = split_folio_to_order(folio, new_order);
> + folio_unlock(folio);
>
> if (ret && release)
> put_page(page);
Ughh.. This patch is broken, sorry.
Please ignore the patchset. Will follow up.
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split
2026-07-14 13:01 ` David Hildenbrand (Arm)
@ 2026-07-14 14:53 ` Kiryl Shutsemau
2026-07-14 14:58 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 13+ messages in thread
From: Kiryl Shutsemau @ 2026-07-14 14:53 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Lorenzo Stoakes, Miaohe Lin, Naoya Horiguchi,
Zi Yan, Baolin Wang, Liam R . Howlett, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Usama Arif, Hao Zhang,
Hao Zhang, linux-mm, linux-kernel, stable
On Tue, Jul 14, 2026 at 03:01:46PM +0200, David Hildenbrand (Arm) wrote:
> On 7/14/26 14:23, Kiryl Shutsemau wrote:
> > From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
> >
> > try_to_split_thp_page() locked the poisoned page and passed it to
> > split_huge_page_to_order(), which returns that very page locked to the
> > caller. For a tail page that means __folio_split() runs with @lock_at
> > pointing into the middle of the folio.
> >
> > __folio_split() dereferences the mapping after the split completes
> > (shmem_uncharge(), i_mmap_unlock_read()). The only thing keeping the
> > inode alive across that is the locked @lock_at folio: while it stays in
> > the page cache, eviction cannot complete.
> >
> > But a tail @lock_at can lie beyond EOF -- e.g. part of a shmem THP that
> > reaches past i_size while the file is being truncated. The split then
> > drops it from the page cache yet still returns it locked, so the pin is
> > gone and a racing final iput() can evict and RCU-free the inode while
> > __folio_split() is still running:
> >
> > BUG: KASAN: slab-use-after-free in __up_read+0x634/0x790
> > i_mmap_unlock_read include/linux/fs.h:537 [inline]
> > __folio_split+0x732/0x1640 mm/huge_memory.c:4100
> > try_to_split_thp_page+0xab/0x390 mm/memory-failure.c:1675
> > memory_failure+0x1394/0x26e0 mm/memory-failure.c:2470
> >
> > Freed by task 4601:
> > shmem_free_in_core_inode+0x54/0xb0 mm/shmem.c:5177
> > evict+0x57f/0xac0 fs/inode.c:870
> >
> > Split the folio as a folio, via split_folio_to_order(), so the head is
> > the anchor left locked. The head is piece 0, which the beyond-EOF drop
> > loop never removes (it starts at folio_next(folio)), so the split always
> > leaves it in the page cache and the inode stays pinned for the whole of
> > __folio_split(). memory_failure() and soft offline re-lock the poisoned
> > subpage's folio themselves after the split, so they do not depend on it
> > being returned locked.
> >
> > Reported-by: Hao Zhang <zhanghao1@kylinos.cn>
> > Closes: https://lore.kernel.org/linux-mm/20260710071344.GA106129@zh-pc
> > Fixes: baa355fd3314 ("thp: file pages support for split_huge_page()")
> > Cc: <stable@vger.kernel.org>
> > Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
> > ---
> > mm/memory-failure.c | 13 ++++++++++---
> > 1 file changed, 10 insertions(+), 3 deletions(-)
> >
> > diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> > index 51508a55c405..68d42cbed458 100644
> > --- a/mm/memory-failure.c
> > +++ b/mm/memory-failure.c
> > @@ -1657,11 +1657,18 @@ static int identify_page_state(unsigned long pfn, struct page *p,
> > static int try_to_split_thp_page(struct page *page, unsigned int new_order,
> > bool release)
> > {
> > + struct folio *folio = page_folio(page);
> > int ret;
> >
> > - lock_page(page);
> > - ret = split_huge_page_to_order(page, new_order);
> > - unlock_page(page);
> > + /*
> > + * Lock and split at the head, not the poisoned subpage: __folio_split()
> > + * keeps the anchor folio locked and needs it to stay in the page cache
> > + * to pin the inode. A tail beyond EOF would be dropped yet returned
> > + * locked, losing that pin. The caller re-locks @page afterwards.
> > + */
> > + folio_lock(folio);
> > + ret = split_folio_to_order(folio, new_order);
> > + folio_unlock(folio);
>
> With a non-uniform split it would actually make a difference: we'd want to split
> such that we the other folio pages minimal.
>
> split_folio_to_order() always seems to end up in
> __split_huge_page_to_list_to_order() where we do a SPLIT_TYPE_UNIFORM.
>
> I recall discussing with Zi and Willy that in the future we'd want to convert
> more places to do a non-uniform split.
>
> So I'm afraid that would just re-introduce the problem then.
Right. Non-uniform split can be useful.
But my patch is completely broken because code expects the pin to be on the
@page, not on the head. put_page() few lines down can explode already.
So the fix does not belong in memory_failure(). It belongs in
__folio_split(), and it is really just 2/5: refuse the split with -EBUSY
when @lock_at is at or beyond the sampled EOF.
The safety then sits in __folio_split() regardless of caller or split type,
which should also cover your non-uniform worry.
The behavioural change is that memory_failure() reports a beyond-EOF
poisoned tail as unsplit (MF_FAILED) instead of recovered, and kills the
mappers instead of splitting the page off. What we give up is salvaging
the folio's healthy pages and the clean unmap -- both low value for a page
that is beyond EOF and getting truncated away. Containment is unaffected:
PageHWPoison is set before the split and free_pages_prepare() keeps a
poisoned page out of the buddy allocator, so the bad page never comes back
regardless.
The alternative, if we would rather keep the recovered outcome, is to leave
@lock_at as the poisoned page and move i_mmap_unlock_read() (and
shmem_uncharge()) ahead of the after-split unlock loop, so every mapping
dereference happens while @folio -- the head, within EOF -- still pins the
inode. That is close to Hao's original patch. It works, but it rests on "no
mapping dereference after the unlock loop", which is its own fragility.
I lean towards the -EBUSY guard.
Comments?
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split
2026-07-14 14:53 ` Kiryl Shutsemau
@ 2026-07-14 14:58 ` David Hildenbrand (Arm)
2026-07-14 15:44 ` Zi Yan
0 siblings, 1 reply; 13+ messages in thread
From: David Hildenbrand (Arm) @ 2026-07-14 14:58 UTC (permalink / raw)
To: Kiryl Shutsemau
Cc: Andrew Morton, Lorenzo Stoakes, Miaohe Lin, Naoya Horiguchi,
Zi Yan, Baolin Wang, Liam R . Howlett, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Usama Arif, Hao Zhang,
Hao Zhang, linux-mm, linux-kernel, stable
On 7/14/26 16:53, Kiryl Shutsemau wrote:
> On Tue, Jul 14, 2026 at 03:01:46PM +0200, David Hildenbrand (Arm) wrote:
>> On 7/14/26 14:23, Kiryl Shutsemau wrote:
>>> From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
>>>
>>> try_to_split_thp_page() locked the poisoned page and passed it to
>>> split_huge_page_to_order(), which returns that very page locked to the
>>> caller. For a tail page that means __folio_split() runs with @lock_at
>>> pointing into the middle of the folio.
>>>
>>> __folio_split() dereferences the mapping after the split completes
>>> (shmem_uncharge(), i_mmap_unlock_read()). The only thing keeping the
>>> inode alive across that is the locked @lock_at folio: while it stays in
>>> the page cache, eviction cannot complete.
>>>
>>> But a tail @lock_at can lie beyond EOF -- e.g. part of a shmem THP that
>>> reaches past i_size while the file is being truncated. The split then
>>> drops it from the page cache yet still returns it locked, so the pin is
>>> gone and a racing final iput() can evict and RCU-free the inode while
>>> __folio_split() is still running:
>>>
>>> BUG: KASAN: slab-use-after-free in __up_read+0x634/0x790
>>> i_mmap_unlock_read include/linux/fs.h:537 [inline]
>>> __folio_split+0x732/0x1640 mm/huge_memory.c:4100
>>> try_to_split_thp_page+0xab/0x390 mm/memory-failure.c:1675
>>> memory_failure+0x1394/0x26e0 mm/memory-failure.c:2470
>>>
>>> Freed by task 4601:
>>> shmem_free_in_core_inode+0x54/0xb0 mm/shmem.c:5177
>>> evict+0x57f/0xac0 fs/inode.c:870
>>>
>>> Split the folio as a folio, via split_folio_to_order(), so the head is
>>> the anchor left locked. The head is piece 0, which the beyond-EOF drop
>>> loop never removes (it starts at folio_next(folio)), so the split always
>>> leaves it in the page cache and the inode stays pinned for the whole of
>>> __folio_split(). memory_failure() and soft offline re-lock the poisoned
>>> subpage's folio themselves after the split, so they do not depend on it
>>> being returned locked.
>>>
>>> Reported-by: Hao Zhang <zhanghao1@kylinos.cn>
>>> Closes: https://lore.kernel.org/linux-mm/20260710071344.GA106129@zh-pc
>>> Fixes: baa355fd3314 ("thp: file pages support for split_huge_page()")
>>> Cc: <stable@vger.kernel.org>
>>> Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
>>> ---
>>> mm/memory-failure.c | 13 ++++++++++---
>>> 1 file changed, 10 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
>>> index 51508a55c405..68d42cbed458 100644
>>> --- a/mm/memory-failure.c
>>> +++ b/mm/memory-failure.c
>>> @@ -1657,11 +1657,18 @@ static int identify_page_state(unsigned long pfn, struct page *p,
>>> static int try_to_split_thp_page(struct page *page, unsigned int new_order,
>>> bool release)
>>> {
>>> + struct folio *folio = page_folio(page);
>>> int ret;
>>>
>>> - lock_page(page);
>>> - ret = split_huge_page_to_order(page, new_order);
>>> - unlock_page(page);
>>> + /*
>>> + * Lock and split at the head, not the poisoned subpage: __folio_split()
>>> + * keeps the anchor folio locked and needs it to stay in the page cache
>>> + * to pin the inode. A tail beyond EOF would be dropped yet returned
>>> + * locked, losing that pin. The caller re-locks @page afterwards.
>>> + */
>>> + folio_lock(folio);
>>> + ret = split_folio_to_order(folio, new_order);
>>> + folio_unlock(folio);
>>
>> With a non-uniform split it would actually make a difference: we'd want to split
>> such that we the other folio pages minimal.
>>
>> split_folio_to_order() always seems to end up in
>> __split_huge_page_to_list_to_order() where we do a SPLIT_TYPE_UNIFORM.
>>
>> I recall discussing with Zi and Willy that in the future we'd want to convert
>> more places to do a non-uniform split.
>>
>> So I'm afraid that would just re-introduce the problem then.
>
> Right. Non-uniform split can be useful.
>
> But my patch is completely broken because code expects the pin to be on the
> @page, not on the head. put_page() few lines down can explode already.
Yeah.
>
> So the fix does not belong in memory_failure(). It belongs in
> __folio_split(), and it is really just 2/5: refuse the split with -EBUSY
> when @lock_at is at or beyond the sampled EOF.
>
> The safety then sits in __folio_split() regardless of caller or split type,
> which should also cover your non-uniform worry.
>
> The behavioural change is that memory_failure() reports a beyond-EOF
> poisoned tail as unsplit (MF_FAILED) instead of recovered, and kills the
> mappers instead of splitting the page off. What we give up is salvaging
> the folio's healthy pages and the clean unmap -- both low value for a page
> that is beyond EOF and getting truncated away. Containment is unaffected:
> PageHWPoison is set before the split and free_pages_prepare() keeps a
> poisoned page out of the buddy allocator, so the bad page never comes back
> regardless.
>
> The alternative, if we would rather keep the recovered outcome, is to leave
> @lock_at as the poisoned page and move i_mmap_unlock_read() (and
> shmem_uncharge()) ahead of the after-split unlock loop, so every mapping
> dereference happens while @folio -- the head, within EOF -- still pins the
> inode. That is close to Hao's original patch. It works, but it rests on "no
> mapping dereference after the unlock loop", which is its own fragility.
At least it can be well documented.
>
> I lean towards the -EBUSY guard.
The latter approach seems cleaner to me, but let's hear others as well.
--
Cheers,
David
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split
2026-07-14 14:58 ` David Hildenbrand (Arm)
@ 2026-07-14 15:44 ` Zi Yan
2026-07-14 16:40 ` Kiryl Shutsemau
0 siblings, 1 reply; 13+ messages in thread
From: Zi Yan @ 2026-07-14 15:44 UTC (permalink / raw)
To: David Hildenbrand (Arm), Kiryl Shutsemau
Cc: Andrew Morton, Lorenzo Stoakes, Miaohe Lin, Naoya Horiguchi,
Baolin Wang, Liam R . Howlett, Nico Pache, Ryan Roberts, Dev Jain,
Barry Song, Lance Yang, Usama Arif, Hao Zhang, Hao Zhang,
linux-mm, linux-kernel, stable
On 14 Jul 2026, at 10:58, David Hildenbrand (Arm) wrote:
> On 7/14/26 16:53, Kiryl Shutsemau wrote:
>> On Tue, Jul 14, 2026 at 03:01:46PM +0200, David Hildenbrand (Arm) wrote:
>>> On 7/14/26 14:23, Kiryl Shutsemau wrote:
>>>> From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
>>>>
>>>> try_to_split_thp_page() locked the poisoned page and passed it to
>>>> split_huge_page_to_order(), which returns that very page locked to the
>>>> caller. For a tail page that means __folio_split() runs with @lock_at
>>>> pointing into the middle of the folio.
>>>>
>>>> __folio_split() dereferences the mapping after the split completes
>>>> (shmem_uncharge(), i_mmap_unlock_read()). The only thing keeping the
>>>> inode alive across that is the locked @lock_at folio: while it stays in
>>>> the page cache, eviction cannot complete.
>>>>
>>>> But a tail @lock_at can lie beyond EOF -- e.g. part of a shmem THP that
>>>> reaches past i_size while the file is being truncated. The split then
>>>> drops it from the page cache yet still returns it locked, so the pin is
>>>> gone and a racing final iput() can evict and RCU-free the inode while
>>>> __folio_split() is still running:
>>>>
>>>> BUG: KASAN: slab-use-after-free in __up_read+0x634/0x790
>>>> i_mmap_unlock_read include/linux/fs.h:537 [inline]
>>>> __folio_split+0x732/0x1640 mm/huge_memory.c:4100
>>>> try_to_split_thp_page+0xab/0x390 mm/memory-failure.c:1675
>>>> memory_failure+0x1394/0x26e0 mm/memory-failure.c:2470
>>>>
>>>> Freed by task 4601:
>>>> shmem_free_in_core_inode+0x54/0xb0 mm/shmem.c:5177
>>>> evict+0x57f/0xac0 fs/inode.c:870
>>>>
>>>> Split the folio as a folio, via split_folio_to_order(), so the head is
>>>> the anchor left locked. The head is piece 0, which the beyond-EOF drop
>>>> loop never removes (it starts at folio_next(folio)), so the split always
>>>> leaves it in the page cache and the inode stays pinned for the whole of
>>>> __folio_split(). memory_failure() and soft offline re-lock the poisoned
>>>> subpage's folio themselves after the split, so they do not depend on it
>>>> being returned locked.
>>>>
>>>> Reported-by: Hao Zhang <zhanghao1@kylinos.cn>
>>>> Closes: https://lore.kernel.org/linux-mm/20260710071344.GA106129@zh-pc
>>>> Fixes: baa355fd3314 ("thp: file pages support for split_huge_page()")
>>>> Cc: <stable@vger.kernel.org>
>>>> Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
>>>> ---
>>>> mm/memory-failure.c | 13 ++++++++++---
>>>> 1 file changed, 10 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
>>>> index 51508a55c405..68d42cbed458 100644
>>>> --- a/mm/memory-failure.c
>>>> +++ b/mm/memory-failure.c
>>>> @@ -1657,11 +1657,18 @@ static int identify_page_state(unsigned long pfn, struct page *p,
>>>> static int try_to_split_thp_page(struct page *page, unsigned int new_order,
>>>> bool release)
>>>> {
>>>> + struct folio *folio = page_folio(page);
>>>> int ret;
>>>>
>>>> - lock_page(page);
>>>> - ret = split_huge_page_to_order(page, new_order);
>>>> - unlock_page(page);
>>>> + /*
>>>> + * Lock and split at the head, not the poisoned subpage: __folio_split()
>>>> + * keeps the anchor folio locked and needs it to stay in the page cache
>>>> + * to pin the inode. A tail beyond EOF would be dropped yet returned
>>>> + * locked, losing that pin. The caller re-locks @page afterwards.
>>>> + */
>>>> + folio_lock(folio);
>>>> + ret = split_folio_to_order(folio, new_order);
>>>> + folio_unlock(folio);
>>>
>>> With a non-uniform split it would actually make a difference: we'd want to split
>>> such that we the other folio pages minimal.
>>>
>>> split_folio_to_order() always seems to end up in
>>> __split_huge_page_to_list_to_order() where we do a SPLIT_TYPE_UNIFORM.
>>>
>>> I recall discussing with Zi and Willy that in the future we'd want to convert
>>> more places to do a non-uniform split.
>>>
>>> So I'm afraid that would just re-introduce the problem then.
>>
>> Right. Non-uniform split can be useful.
>>
>> But my patch is completely broken because code expects the pin to be on the
>> @page, not on the head. put_page() few lines down can explode already.
>
> Yeah.
>
>>
>> So the fix does not belong in memory_failure(). It belongs in
>> __folio_split(), and it is really just 2/5: refuse the split with -EBUSY
>> when @lock_at is at or beyond the sampled EOF.
There is an alternative, only igrab() when @lock_at is at or beyond the EOF,
as I was bouncing ideas with Codex.
Some subtlety exists in these two “@lock_at at or beyond EOF” approaches.
When @lock_at can be a tail page of an after-split folio, Patch 2 returns
-EBUSY unnecessarily, since the after-split folio containing @lock_at will
be still in xarray, preventing inode going away. To make a precise decision,
we will need to determine the index of the after-split folio containing @lock_at
when checking against end. It is easy for uniform split, namely
lock_at_folio_index =
folio->index + round_down(lock_at - &folio->page, 1UL << new_order);
but for non-uniform split, the calculation is more involved and I have not
figured it out yet.
If we go “return -EBUSY if @lock_at index is at or beyond EOF”, are we OK
with not being able to split a folio when it is actually splittable?
>>
>> The safety then sits in __folio_split() regardless of caller or split type,
>> which should also cover your non-uniform worry.
>>
>> The behavioural change is that memory_failure() reports a beyond-EOF
>> poisoned tail as unsplit (MF_FAILED) instead of recovered, and kills the
>> mappers instead of splitting the page off. What we give up is salvaging
>> the folio's healthy pages and the clean unmap -- both low value for a page
>> that is beyond EOF and getting truncated away. Containment is unaffected:
>> PageHWPoison is set before the split and free_pages_prepare() keeps a
>> poisoned page out of the buddy allocator, so the bad page never comes back
>> regardless.
>>
>> The alternative, if we would rather keep the recovered outcome, is to leave
>> @lock_at as the poisoned page and move i_mmap_unlock_read() (and
>> shmem_uncharge()) ahead of the after-split unlock loop, so every mapping
>> dereference happens while @folio -- the head, within EOF -- still pins the
>> inode. That is close to Hao's original patch. It works, but it rests on "no
>> mapping dereference after the unlock loop", which is its own fragility.
>
> At least it can be well documented.
This approach works without changing existing behavior, as long as we document
the requirement well.
I admit that this is another implicit synchronization in folio split process,
in addition to
1) keeping original folio frozen until xarray is updated and
2) do not update xarray with after-split folios until after-split folios are
unfrozen.
>
>>
>> I lean towards the -EBUSY guard.
>
> The latter approach seems cleaner to me, but let's hear others as well.
I prefer moving i_mmap_unlock_read() before the unlock loop (shmem_uncharge()
is already before the loop), but I am more than happy to be convinced that
“return -EBUSY” is better or something else.
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split
2026-07-14 15:44 ` Zi Yan
@ 2026-07-14 16:40 ` Kiryl Shutsemau
2026-07-14 17:31 ` Zi Yan
0 siblings, 1 reply; 13+ messages in thread
From: Kiryl Shutsemau @ 2026-07-14 16:40 UTC (permalink / raw)
To: Zi Yan
Cc: David Hildenbrand (Arm), Andrew Morton, Lorenzo Stoakes,
Miaohe Lin, Naoya Horiguchi, Baolin Wang, Liam R . Howlett,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Hao Zhang, Hao Zhang, linux-mm, linux-kernel, stable
On Tue, Jul 14, 2026 at 11:44:39AM -0400, Zi Yan wrote:
> There is an alternative, only igrab() when @lock_at is at or beyond the EOF,
> as I was bouncing ideas with Codex.
I saw this option too, but I wound rather not go this path.
iput() still can lead to inode eviction an bunch of random filesystem
complexity under us. I don't think we want to think about other
fs-related locking issues in split context.
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split
2026-07-14 16:40 ` Kiryl Shutsemau
@ 2026-07-14 17:31 ` Zi Yan
0 siblings, 0 replies; 13+ messages in thread
From: Zi Yan @ 2026-07-14 17:31 UTC (permalink / raw)
To: Kiryl Shutsemau
Cc: David Hildenbrand (Arm), Andrew Morton, Lorenzo Stoakes,
Miaohe Lin, Naoya Horiguchi, Baolin Wang, Liam R . Howlett,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Hao Zhang, Hao Zhang, linux-mm, linux-kernel, stable
On Tue Jul 14, 2026 at 12:40 PM EDT, Kiryl Shutsemau wrote:
> On Tue, Jul 14, 2026 at 11:44:39AM -0400, Zi Yan wrote:
>> There is an alternative, only igrab() when @lock_at is at or beyond the EOF,
>> as I was bouncing ideas with Codex.
>
> I saw this option too, but I wound rather not go this path.
>
> iput() still can lead to inode eviction an bunch of random filesystem
> complexity under us. I don't think we want to think about other
> fs-related locking issues in split context.
Your reasoning makes sense to me. Let's ignore this option.
For your patch 2, we might want something like below to avoid over
rejecting splits. WDYT?
offset = folio_page_idx(folio, lock_at);
if (split_type == SPLIT_TYPE_UNIFORM)
lock_at_index = folio->index + round_down(offset, 1UL << new_order);
else
/* @lock_at in non uniform split is always @folio */
lock_at_index = folio->index;
if (lock_at_index >= end) {
ret = -EBUSY;
goto out_unlock;
}
Also to keep a record in case we want to have @lock_at pointing to any
tail page for non uniform split in the future, something like below is
going to be useful (assisted by Codex).
static pgoff_t split_lock_at_index(struct folio *folio,
unsigned int new_order, struct page *split_at,
struct page *lock_at, enum split_type split_type)
{
unsigned long lock = folio_page_idx(folio, lock_at);
unsigned long split = folio_page_idx(folio, split_at);
int order;
if (split_type == SPLIT_TYPE_UNIFORM)
return folio->index + round_down(lock, 1UL << new_order);
for (order = folio_order(folio) - 1; order >= new_order; order--) {
unsigned long size = 1UL << order;
unsigned long lock_base = round_down(lock, size);
unsigned long split_base = round_down(split, size);
/* folio containing @lock_at will not be split any more */
if (lock_base != split_base)
return folio->index + lock_base;
}
return folio->index + round_down(lock, 1UL << new_order);
}
if (split_lock_at_index(folio, new_order, split_at, lock_at, split_type) >= end) {
ret = -EBUSY;
goto out_unlock;
}
--
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-07-14 17:32 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-14 12:23 [PATCH v2 0/5] mm: fix inode UAF when splitting a file folio past EOF Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 1/5] mm/memory-failure: keep the folio, not the poisoned subpage, locked across split Kiryl Shutsemau
2026-07-14 13:01 ` David Hildenbrand (Arm)
2026-07-14 14:53 ` Kiryl Shutsemau
2026-07-14 14:58 ` David Hildenbrand (Arm)
2026-07-14 15:44 ` Zi Yan
2026-07-14 16:40 ` Kiryl Shutsemau
2026-07-14 17:31 ` Zi Yan
2026-07-14 13:05 ` Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 2/5] mm/huge_memory: refuse to split a file folio when the anchor is beyond EOF Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 3/5] mm/huge_memory: remove unused split_huge_page_to_order() Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 4/5] mm/huge_memory: remove unused can_split_folio() Kiryl Shutsemau
2026-07-14 12:23 ` [PATCH v2 5/5] mm/huge_memory: fold split_folio_to_list_to_order() into split_folio_to_order() Kiryl Shutsemau
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox