ANDROID: 16K: Handle pgoff > file_size for shmem and file backed VMAs

VMAs can be created to be larger than the backing file: See [1]
(This is also true for shmem backed VMAs.)

Faulting off the end of a file will result in SIGBUS since there
is no file page for the given file offset.

shmem pages live in page cache or swap cache. Looking up a page cache
page with an index (pgoff) beyond the file is invalid and will result
in shmem_get_folio_gfp() returning -EINVAL.

Insert fixup anon vmas for such file backed and shmem backed VMAs,
to prevent the invalid faults in emulated 16kB mode.

Although the 16K emulators currently use ext4 for the /data partition,
also handle the case for f2fs which overrides the default filemap_fault
with f2f2_filemap_fault().

[1] https://r.android.com/2967716

Bug: 383389337
Bug: 355274435
Test: adb root && adb shell &&
      adb shell cp /system/bin/strace /dev/ &&
      adb shell /dev/strace -h
Change-Id: Iede02e82fc5958b3aafaf75383404f2f7f59bb23
Signed-off-by: Kalesh Singh <kaleshsingh@google.com>
This commit is contained in:
Kalesh Singh
2024-07-26 14:19:20 -07:00
committed by Carlos Llamas
parent cd48f9a1f7
commit 5c1d7ef671
4 changed files with 50 additions and 4 deletions

View File

@@ -38,7 +38,7 @@
#undef CREATE_TRACE_POINTS
#include <trace/hooks/fs.h>
static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf)
vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf)
{
struct inode *inode = file_inode(vmf->vma->vm_file);
vm_flags_t flags = vmf->vma->vm_flags;

View File

@@ -40,6 +40,14 @@
DECLARE_STATIC_KEY_FALSE(page_shift_compat_enabled);
extern int page_shift_compat __ro_after_init;
#ifdef CONFIG_SHMEM
extern vm_fault_t shmem_fault(struct vm_fault *vmf);
#endif /* CONFIG_SHMEM */
#ifdef CONFIG_F2FS_FS
extern vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf);
#endif /* CONFIG_F2FS_FS */
#ifdef CONFIG_X86_64
static __always_inline unsigned __page_shift(void)
{

View File

@@ -96,6 +96,29 @@ unsigned long ___filemap_len(struct inode *inode, unsigned long pgoff, unsigned
return len;
}
static inline bool is_shmem_fault(const struct vm_operations_struct *vm_ops)
{
#ifdef CONFIG_SHMEM
return vm_ops->fault == shmem_fault;
#else
return false;
#endif
}
static inline bool is_f2fs_filemap_fault(const struct vm_operations_struct *vm_ops)
{
#ifdef CONFIG_F2FS_FS
return vm_ops->fault == f2fs_filemap_fault;
#else
return false;
#endif
}
static inline bool is_filemap_fault(const struct vm_operations_struct *vm_ops)
{
return vm_ops->fault == filemap_fault;
}
/*
* This is called to fill any holes created by ___filemap_len()
* with an anonymous mapping.
@@ -108,6 +131,7 @@ void ___filemap_fixup(unsigned long addr, unsigned long prot, unsigned long old_
struct mm_struct *mm = current->mm;
unsigned long populate = 0;
struct vm_area_struct *vma;
const struct vm_operations_struct *vm_ops;
if (!anon_len)
return;
@@ -126,8 +150,22 @@ void ___filemap_fixup(unsigned long addr, unsigned long prot, unsigned long old_
*/
BUG_ON(!vma);
/* Only handle fixups for filemap faults */
if (vma->vm_ops && vma->vm_ops->fault != filemap_fault)
vm_ops = vma->vm_ops;
if (!vm_ops)
return;
/*
* Insert fixup vmas for file backed and shmem backed VMAs.
*
* Faulting off the end of a file will result in SIGBUS since there is no
* file page for the given file offset.
*
* shmem pages live in page cache or swap cache. Looking up a page cache
* page with an index (pgoff) beyond the file is invalid and will result
* in shmem_get_folio_gfp() returning -EINVAL.
*/
if (!is_filemap_fault(vm_ops) && !is_f2fs_filemap_fault(vm_ops) &&
!is_shmem_fault(vm_ops))
return;
/*

View File

@@ -2538,7 +2538,7 @@ static vm_fault_t shmem_falloc_wait(struct vm_fault *vmf, struct inode *inode)
return ret;
}
static vm_fault_t shmem_fault(struct vm_fault *vmf)
vm_fault_t shmem_fault(struct vm_fault *vmf)
{
struct inode *inode = file_inode(vmf->vma->vm_file);
gfp_t gfp = mapping_gfp_mask(inode->i_mapping);