Skip to content

Commit a6226fa

Browse files
Kiryl Shutsemaugregkh
authored andcommitted
mm/truncate: unmap large folio on split failure
commit fa04f5b 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/[email protected] Fixes: b9a8a41 ("truncate,shmem: Handle truncates that split large folios") Signed-off-by: Kiryl Shutsemau <[email protected]> Cc: Al Viro <[email protected]> Cc: Baolin Wang <[email protected]> Cc: Christian Brauner <[email protected]> Cc: "Darrick J. Wong" <[email protected]> Cc: Dave Chinner <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: Liam Howlett <[email protected]> Cc: Lorenzo Stoakes <[email protected]> Cc: Matthew Wilcox (Oracle) <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Mike Rapoport <[email protected]> Cc: Rik van Riel <[email protected]> Cc: Shakeel Butt <[email protected]> Cc: Suren Baghdasaryan <[email protected]> Cc: Vlastimil Babka <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Kiryl Shutsemau <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent c4476fa commit a6226fa

File tree

1 file changed

+26
-1
lines changed

1 file changed

+26
-1
lines changed

mm/truncate.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,31 @@ int truncate_inode_folio(struct address_space *mapping, struct folio *folio)
179179
return 0;
180180
}
181181

182+
static int try_folio_split_or_unmap(struct folio *folio)
183+
{
184+
enum ttu_flags ttu_flags =
185+
TTU_SYNC |
186+
TTU_SPLIT_HUGE_PMD |
187+
TTU_IGNORE_MLOCK;
188+
int ret;
189+
190+
ret = split_folio(folio);
191+
192+
/*
193+
* If the split fails, unmap the folio, so it will be refaulted
194+
* with PTEs to respect SIGBUS semantics.
195+
*
196+
* Make an exception for shmem/tmpfs that for long time
197+
* intentionally mapped with PMDs across i_size.
198+
*/
199+
if (ret && !shmem_mapping(folio->mapping)) {
200+
try_to_unmap(folio, ttu_flags);
201+
WARN_ON(folio_mapped(folio));
202+
}
203+
204+
return ret;
205+
}
206+
182207
/*
183208
* Handle partial folios. The folio may be entirely within the
184209
* 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)
223248
folio_invalidate(folio, offset, length);
224249
if (!folio_test_large(folio))
225250
return true;
226-
if (split_folio(folio) == 0)
251+
if (try_folio_split_or_unmap(folio) == 0)
227252
return true;
228253
if (folio_test_dirty(folio))
229254
return false;

0 commit comments

Comments
 (0)