Skip to content

Commit fa04f5b

Browse files
Kiryl Shutsemauakpm00
authored andcommitted
mm/truncate: unmap large folio on split failure
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: b9a8a41 ("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>
1 parent 74207de commit fa04f5b

File tree

1 file changed

+29
-6
lines changed

1 file changed

+29
-6
lines changed

mm/truncate.c

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,32 @@ int truncate_inode_folio(struct address_space *mapping, struct folio *folio)
177177
return 0;
178178
}
179179

180+
static int try_folio_split_or_unmap(struct folio *folio, struct page *split_at,
181+
unsigned long min_order)
182+
{
183+
enum ttu_flags ttu_flags =
184+
TTU_SYNC |
185+
TTU_SPLIT_HUGE_PMD |
186+
TTU_IGNORE_MLOCK;
187+
int ret;
188+
189+
ret = try_folio_split_to_order(folio, split_at, min_order);
190+
191+
/*
192+
* If the split fails, unmap the folio, so it will be refaulted
193+
* with PTEs to respect SIGBUS semantics.
194+
*
195+
* Make an exception for shmem/tmpfs that for long time
196+
* intentionally mapped with PMDs across i_size.
197+
*/
198+
if (ret && !shmem_mapping(folio->mapping)) {
199+
try_to_unmap(folio, ttu_flags);
200+
WARN_ON(folio_mapped(folio));
201+
}
202+
203+
return ret;
204+
}
205+
180206
/*
181207
* Handle partial folios. The folio may be entirely within the
182208
* range if a split has raced with us. If not, we zero the part of the
@@ -226,7 +252,7 @@ bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end)
226252

227253
min_order = mapping_min_folio_order(folio->mapping);
228254
split_at = folio_page(folio, PAGE_ALIGN_DOWN(offset) / PAGE_SIZE);
229-
if (!try_folio_split_to_order(folio, split_at, min_order)) {
255+
if (!try_folio_split_or_unmap(folio, split_at, min_order)) {
230256
/*
231257
* try to split at offset + length to make sure folios within
232258
* the range can be dropped, especially to avoid memory waste
@@ -250,13 +276,10 @@ bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end)
250276
if (!folio_trylock(folio2))
251277
goto out;
252278

253-
/*
254-
* make sure folio2 is large and does not change its mapping.
255-
* Its split result does not matter here.
256-
*/
279+
/* make sure folio2 is large and does not change its mapping */
257280
if (folio_test_large(folio2) &&
258281
folio2->mapping == folio->mapping)
259-
try_folio_split_to_order(folio2, split_at2, min_order);
282+
try_folio_split_or_unmap(folio2, split_at2, min_order);
260283

261284
folio_unlock(folio2);
262285
out:

0 commit comments

Comments
 (0)