]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
mm/truncate: unmap large folio on split failure
authorKiryl Shutsemau <kas@kernel.org>
Mon, 27 Oct 2025 11:56:36 +0000 (11:56 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 24 Nov 2025 09:36:07 +0000 (10:36 +0100)
commit fa04f5b60fda62c98a53a60de3a1e763f11feb41 upstream.

Accesses within VMA, but beyond i_size rounded up to PAGE_SIZE are
supposed to generate SIGBUS.

This behavior might not be respected on truncation.

During truncation, the kernel splits a large folio in order to reclaim
memory.  As a side effect, it unmaps the folio and destroys PMD mappings
of the folio.  The folio will be refaulted as PTEs and SIGBUS semantics
are preserved.

However, if the split fails, PMD mappings are preserved and the user will
not receive SIGBUS on any accesses within the PMD.

Unmap the folio on split failure.  It will lead to refault as PTEs and
preserve SIGBUS semantics.

Make an exception for shmem/tmpfs that for long time intentionally mapped
with PMDs across i_size.

Link: https://lkml.kernel.org/r/20251027115636.82382-3-kirill@shutemov.name
Fixes: b9a8a4195c7d ("truncate,shmem: Handle truncates that split large folios")
Signed-off-by: Kiryl Shutsemau <kas@kernel.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: "Darrick J. Wong" <djwong@kernel.org>
Cc: Dave Chinner <david@fromorbit.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Rik van Riel <riel@surriel.com>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Kiryl Shutsemau <kas@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
mm/truncate.c

index 0668cd340a46304f62525597374e5928a51d3f0f..fb5c20b57bd4f9f22ea87c68379a7e81f220d221 100644 (file)
@@ -179,6 +179,31 @@ int truncate_inode_folio(struct address_space *mapping, struct folio *folio)
        return 0;
 }
 
+static int try_folio_split_or_unmap(struct folio *folio)
+{
+       enum ttu_flags ttu_flags =
+               TTU_SYNC |
+               TTU_SPLIT_HUGE_PMD |
+               TTU_IGNORE_MLOCK;
+       int ret;
+
+       ret = split_folio(folio);
+
+       /*
+        * If the split fails, unmap the folio, so it will be refaulted
+        * with PTEs to respect SIGBUS semantics.
+        *
+        * Make an exception for shmem/tmpfs that for long time
+        * intentionally mapped with PMDs across i_size.
+        */
+       if (ret && !shmem_mapping(folio->mapping)) {
+               try_to_unmap(folio, ttu_flags);
+               WARN_ON(folio_mapped(folio));
+       }
+
+       return ret;
+}
+
 /*
  * Handle partial folios.  The folio may be entirely within the
  * range if a split has raced with us.  If not, we zero the part of the
@@ -223,7 +248,7 @@ bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end)
                folio_invalidate(folio, offset, length);
        if (!folio_test_large(folio))
                return true;
-       if (split_folio(folio) == 0)
+       if (try_folio_split_or_unmap(folio) == 0)
                return true;
        if (folio_test_dirty(folio))
                return false;