Re: [PATCH 09/16] mm/shmem: preserve data in clean folios when splitting fails

From: Zi Yan

Date: Mon Oct 26 2026 - 22:01:16 EST


On Mon Oct 26, 2026 at 5:40 AM EDT, David Hildenbrand (Arm) wrote:
> From: Chris Mason <mason@xxxxxxxxxx>
>
> A shared writable mapping can have dirty PTEs while its shmem folio is
> still clean. A read fault can install a writable PTE, and a later store
> sets the PTE's dirty bit without marking the folio dirty.
>
> After shmem_undo_range() has handled the boundary folios, a concurrent
> read fault into a hole below the new EOF can allocate a large folio.
> For example, on a tmpfs mount with huge=always, the fault's offset must
> be within the file, but the allocated folio can extend beyond i_size.
> The final scan then finds this folio and calls
> truncate_inode_partial_folio() without marking it dirty.
>
> Splitting normally collects the PTE dirty bits when unmapping the
> folio. An extra reference, for example from vmsplice(), can make it
> fail before that step. The fallback unmapping on split failure excludes
> shmem, so the helper still sees a clean folio and drops it, losing data
> outside the range.
>
> Mark large folios dirty before partial truncation in the final scan,
> as the boundary handling already does. Exclude failed-fallocate
> rollback, which must discard the folios it added.
>
> Fix generated by an LLM and sent by Chris Mason in private.
>
> Fixes: b9a8a4195c7d ("truncate,shmem: Handle truncates that split large folios")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Chris Mason <mason@xxxxxxxxxx>
> [ david: simplify descriptions, adjust comments ]
> Signed-off-by: David Hildenbrand (Arm) <david@xxxxxxxxxx>
> ---
> mm/shmem.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>

Reviewed-by: Zi Yan <ziy@xxxxxxxxxx>

This fix is still needed, since Zhang Yi's patches do not fix this
issue. The patch below is rebased on top of mm-stable.

diff --git a/mm/shmem.c b/mm/shmem.c
index efd54a624974c..e9f98d526fc52 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1463,6 +1463,15 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
VM_BUG_ON_FOLIO(folio_test_writeback(folio),
folio);

+ /*
+ * A shared mapping can have dirty PTEs while
+ * the folio is still clean. Mark the folio
+ * dirty so a failed split cannot discard data
+ * outside the range.
+ */
+ if (!unfalloc && folio_test_large(folio))
+ folio_mark_dirty(folio);
+
if (!folio_test_large(folio)) {
truncate_inode_folio(mapping, folio);
} else if (truncate_inode_partial_folio(folio,



--
Best Regards,
Yan, Zi