Skip to content

Commit a2b5df4

Browse files
Kiryl Shutsemaugregkh
authored andcommitted
mm/memory: do not populate page table entries beyond i_size
commit 74207de2ba10c2973334906822dc94d2e859ffc5 upstream. Patch series "Fix SIGBUS semantics with large folios", v3. Accessing memory within a VMA, but beyond i_size rounded up to the next page size, is supposed to generate SIGBUS. Darrick reported[1] an xfstests regression in v6.18-rc1. generic/749 failed due to missing SIGBUS. This was caused by my recent changes that try to fault in the whole folio where possible: 19773df031bc ("mm/fault: try to map the entire file folio in finish_fault()") 357b92761d94 ("mm/filemap: map entire large folio faultaround") These changes did not consider i_size when setting up PTEs, leading to xfstest breakage. However, the problem has been present in the kernel for a long time - since huge tmpfs was introduced in 2016. The kernel happily maps PMD-sized folios as PMD without checking i_size. And huge=always tmpfs allocates PMD-size folios on any writes. I considered this corner case when I implemented a large tmpfs, and my conclusion was that no one in their right mind should rely on receiving a SIGBUS signal when accessing beyond i_size. I cannot imagine how it could be useful for the workload. But apparently filesystem folks care a lot about preserving strict SIGBUS semantics. Generic/749 was introduced last year with reference to POSIX, but no real workloads were mentioned. It also acknowledged the tmpfs deviation from the test case. POSIX indeed says[3]: References within the address range starting at pa and continuing for len bytes to whole pages following the end of an object shall result in delivery of a SIGBUS signal. The patchset fixes the regression introduced by recent changes as well as more subtle SIGBUS breakage due to split failure on truncation. This patch (of 2): Accesses within VMA, but beyond i_size rounded up to PAGE_SIZE are supposed to generate SIGBUS. Recent changes attempted to fault in full folio where possible. They did not respect i_size, which led to populating PTEs beyond i_size and breaking SIGBUS semantics. Darrick reported generic/749 breakage because of this. However, the problem existed before the recent changes. With huge=always tmpfs, any write to a file leads to PMD-size allocation. Following the fault-in of the folio will install PMD mapping regardless of i_size. Fix filemap_map_pages() and finish_fault() to not install: - PTEs beyond i_size; - PMD mappings across i_size; 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] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Kiryl Shutsemau <[email protected]> Fixes: 6795801 ("xfs: Support large folios") Reported-by: "Darrick J. Wong" <[email protected]> Cc: Al Viro <[email protected]> Cc: Baolin Wang <[email protected]> Cc: Christian Brauner <[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 6194db7 commit a2b5df4

2 files changed

Lines changed: 34 additions & 6 deletions

File tree

mm/filemap.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3743,13 +3743,27 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
37433743
unsigned long rss = 0;
37443744
unsigned int nr_pages = 0, folio_type;
37453745
unsigned short mmap_miss = 0, mmap_miss_saved;
3746+
bool can_map_large;
37463747

37473748
rcu_read_lock();
37483749
folio = next_uptodate_folio(&xas, mapping, end_pgoff);
37493750
if (!folio)
37503751
goto out;
37513752

3752-
if (filemap_map_pmd(vmf, folio, start_pgoff)) {
3753+
file_end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE) - 1;
3754+
end_pgoff = min(end_pgoff, file_end);
3755+
3756+
/*
3757+
* Do not allow to map with PTEs beyond i_size and with PMD
3758+
* across i_size to preserve SIGBUS semantics.
3759+
*
3760+
* Make an exception for shmem/tmpfs that for long time
3761+
* intentionally mapped with PMDs across i_size.
3762+
*/
3763+
can_map_large = shmem_mapping(mapping) ||
3764+
file_end >= folio_next_index(folio);
3765+
3766+
if (can_map_large && filemap_map_pmd(vmf, folio, start_pgoff)) {
37533767
ret = VM_FAULT_NOPAGE;
37543768
goto out;
37553769
}
@@ -3762,10 +3776,6 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
37623776
goto out;
37633777
}
37643778

3765-
file_end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE) - 1;
3766-
if (end_pgoff > file_end)
3767-
end_pgoff = file_end;
3768-
37693779
folio_type = mm_counter_file(folio);
37703780
do {
37713781
unsigned long end;

mm/memory.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
#include <linux/gfp.h>
6666
#include <linux/migrate.h>
6767
#include <linux/string.h>
68+
#include <linux/shmem_fs.h>
6869
#include <linux/memory-tiers.h>
6970
#include <linux/debugfs.h>
7071
#include <linux/userfaultfd_k.h>
@@ -5371,8 +5372,25 @@ vm_fault_t finish_fault(struct vm_fault *vmf)
53715372
return ret;
53725373
}
53735374

5375+
if (!needs_fallback && vma->vm_file) {
5376+
struct address_space *mapping = vma->vm_file->f_mapping;
5377+
pgoff_t file_end;
5378+
5379+
file_end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE);
5380+
5381+
/*
5382+
* Do not allow to map with PTEs beyond i_size and with PMD
5383+
* across i_size to preserve SIGBUS semantics.
5384+
*
5385+
* Make an exception for shmem/tmpfs that for long time
5386+
* intentionally mapped with PMDs across i_size.
5387+
*/
5388+
needs_fallback = !shmem_mapping(mapping) &&
5389+
file_end < folio_next_index(folio);
5390+
}
5391+
53745392
if (pmd_none(*vmf->pmd)) {
5375-
if (folio_test_pmd_mappable(folio)) {
5393+
if (!needs_fallback && folio_test_pmd_mappable(folio)) {
53765394
ret = do_set_pmd(vmf, folio, page);
53775395
if (ret != VM_FAULT_FALLBACK)
53785396
return ret;

0 commit comments

Comments
 (0)