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:
committed by
Carlos Llamas
parent
cd48f9a1f7
commit
5c1d7ef671
+1
-1
@@ -38,7 +38,7 @@
|
|||||||
#undef CREATE_TRACE_POINTS
|
#undef CREATE_TRACE_POINTS
|
||||||
#include <trace/hooks/fs.h>
|
#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);
|
struct inode *inode = file_inode(vmf->vma->vm_file);
|
||||||
vm_flags_t flags = vmf->vma->vm_flags;
|
vm_flags_t flags = vmf->vma->vm_flags;
|
||||||
|
|||||||
@@ -40,6 +40,14 @@
|
|||||||
DECLARE_STATIC_KEY_FALSE(page_shift_compat_enabled);
|
DECLARE_STATIC_KEY_FALSE(page_shift_compat_enabled);
|
||||||
extern int page_shift_compat __ro_after_init;
|
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
|
#ifdef CONFIG_X86_64
|
||||||
static __always_inline unsigned __page_shift(void)
|
static __always_inline unsigned __page_shift(void)
|
||||||
{
|
{
|
||||||
|
|||||||
+40
-2
@@ -96,6 +96,29 @@ unsigned long ___filemap_len(struct inode *inode, unsigned long pgoff, unsigned
|
|||||||
return len;
|
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()
|
* This is called to fill any holes created by ___filemap_len()
|
||||||
* with an anonymous mapping.
|
* 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;
|
struct mm_struct *mm = current->mm;
|
||||||
unsigned long populate = 0;
|
unsigned long populate = 0;
|
||||||
struct vm_area_struct *vma;
|
struct vm_area_struct *vma;
|
||||||
|
const struct vm_operations_struct *vm_ops;
|
||||||
|
|
||||||
if (!anon_len)
|
if (!anon_len)
|
||||||
return;
|
return;
|
||||||
@@ -126,8 +150,22 @@ void ___filemap_fixup(unsigned long addr, unsigned long prot, unsigned long old_
|
|||||||
*/
|
*/
|
||||||
BUG_ON(!vma);
|
BUG_ON(!vma);
|
||||||
|
|
||||||
/* Only handle fixups for filemap faults */
|
vm_ops = vma->vm_ops;
|
||||||
if (vma->vm_ops && vma->vm_ops->fault != filemap_fault)
|
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;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
+1
-1
@@ -2538,7 +2538,7 @@ static vm_fault_t shmem_falloc_wait(struct vm_fault *vmf, struct inode *inode)
|
|||||||
return ret;
|
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);
|
struct inode *inode = file_inode(vmf->vma->vm_file);
|
||||||
gfp_t gfp = mapping_gfp_mask(inode->i_mapping);
|
gfp_t gfp = mapping_gfp_mask(inode->i_mapping);
|
||||||
|
|||||||
Reference in New Issue
Block a user