Skip to content

Commit e2a50c1

Browse files
aagittorvalds
authored andcommitted
userfaultfd: shmem: add i_size checks
With MAP_SHARED: recheck the i_size after taking the PT lock, to serialize against truncate with the PT lock. Delete the page from the pagecache if the i_size_read check fails. With MAP_PRIVATE: check the i_size after the PT lock before mapping anonymous memory or zeropages into the MAP_PRIVATE shmem mapping. A mostly irrelevant cleanup: like we do the delete_from_page_cache() pagecache removal after dropping the PT lock, the PT lock is a spinlock so drop it before the sleepable page lock. Link: http://lkml.kernel.org/r/20181126173452.26955-5-aarcange@redhat.com Fixes: 4c27fe4 ("userfaultfd: shmem: add shmem_mcopy_atomic_pte for userfaultfd support") Signed-off-by: Andrea Arcangeli <aarcange@redhat.com> Reviewed-by: Mike Rapoport <rppt@linux.ibm.com> Reviewed-by: Hugh Dickins <hughd@google.com> Reported-by: Jann Horn <jannh@google.com> Cc: <stable@vger.kernel.org> Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com> Cc: Mike Kravetz <mike.kravetz@oracle.com> Cc: Peter Xu <peterx@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 29ec906 commit e2a50c1

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

mm/shmem.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2216,6 +2216,7 @@ static int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,
22162216
struct page *page;
22172217
pte_t _dst_pte, *dst_pte;
22182218
int ret;
2219+
pgoff_t offset, max_off;
22192220

22202221
ret = -ENOMEM;
22212222
if (!shmem_inode_acct_block(inode, 1))
@@ -2253,6 +2254,12 @@ static int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,
22532254
__SetPageSwapBacked(page);
22542255
__SetPageUptodate(page);
22552256

2257+
ret = -EFAULT;
2258+
offset = linear_page_index(dst_vma, dst_addr);
2259+
max_off = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
2260+
if (unlikely(offset >= max_off))
2261+
goto out_release;
2262+
22562263
ret = mem_cgroup_try_charge_delay(page, dst_mm, gfp, &memcg, false);
22572264
if (ret)
22582265
goto out_release;
@@ -2268,8 +2275,14 @@ static int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,
22682275
if (dst_vma->vm_flags & VM_WRITE)
22692276
_dst_pte = pte_mkwrite(pte_mkdirty(_dst_pte));
22702277

2271-
ret = -EEXIST;
22722278
dst_pte = pte_offset_map_lock(dst_mm, dst_pmd, dst_addr, &ptl);
2279+
2280+
ret = -EFAULT;
2281+
max_off = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
2282+
if (unlikely(offset >= max_off))
2283+
goto out_release_uncharge_unlock;
2284+
2285+
ret = -EEXIST;
22732286
if (!pte_none(*dst_pte))
22742287
goto out_release_uncharge_unlock;
22752288

@@ -2287,13 +2300,14 @@ static int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,
22872300

22882301
/* No need to invalidate - it was non-present before */
22892302
update_mmu_cache(dst_vma, dst_addr, dst_pte);
2290-
unlock_page(page);
22912303
pte_unmap_unlock(dst_pte, ptl);
2304+
unlock_page(page);
22922305
ret = 0;
22932306
out:
22942307
return ret;
22952308
out_release_uncharge_unlock:
22962309
pte_unmap_unlock(dst_pte, ptl);
2310+
delete_from_page_cache(page);
22972311
out_release_uncharge:
22982312
mem_cgroup_cancel_charge(page, memcg, false);
22992313
out_release:

mm/userfaultfd.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ static int mcopy_atomic_pte(struct mm_struct *dst_mm,
3333
void *page_kaddr;
3434
int ret;
3535
struct page *page;
36+
pgoff_t offset, max_off;
37+
struct inode *inode;
3638

3739
if (!*pagep) {
3840
ret = -ENOMEM;
@@ -73,8 +75,17 @@ static int mcopy_atomic_pte(struct mm_struct *dst_mm,
7375
if (dst_vma->vm_flags & VM_WRITE)
7476
_dst_pte = pte_mkwrite(pte_mkdirty(_dst_pte));
7577

76-
ret = -EEXIST;
7778
dst_pte = pte_offset_map_lock(dst_mm, dst_pmd, dst_addr, &ptl);
79+
if (dst_vma->vm_file) {
80+
/* the shmem MAP_PRIVATE case requires checking the i_size */
81+
inode = dst_vma->vm_file->f_inode;
82+
offset = linear_page_index(dst_vma, dst_addr);
83+
max_off = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
84+
ret = -EFAULT;
85+
if (unlikely(offset >= max_off))
86+
goto out_release_uncharge_unlock;
87+
}
88+
ret = -EEXIST;
7889
if (!pte_none(*dst_pte))
7990
goto out_release_uncharge_unlock;
8091

@@ -108,11 +119,22 @@ static int mfill_zeropage_pte(struct mm_struct *dst_mm,
108119
pte_t _dst_pte, *dst_pte;
109120
spinlock_t *ptl;
110121
int ret;
122+
pgoff_t offset, max_off;
123+
struct inode *inode;
111124

112125
_dst_pte = pte_mkspecial(pfn_pte(my_zero_pfn(dst_addr),
113126
dst_vma->vm_page_prot));
114-
ret = -EEXIST;
115127
dst_pte = pte_offset_map_lock(dst_mm, dst_pmd, dst_addr, &ptl);
128+
if (dst_vma->vm_file) {
129+
/* the shmem MAP_PRIVATE case requires checking the i_size */
130+
inode = dst_vma->vm_file->f_inode;
131+
offset = linear_page_index(dst_vma, dst_addr);
132+
max_off = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
133+
ret = -EFAULT;
134+
if (unlikely(offset >= max_off))
135+
goto out_unlock;
136+
}
137+
ret = -EEXIST;
116138
if (!pte_none(*dst_pte))
117139
goto out_unlock;
118140
set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte);

0 commit comments

Comments
 (0)