From: "Kiryl Shutsemau (Meta)" __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: Signed-off-by: Kiryl Shutsemau (Meta) --- 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