Skip to content

Commit 3f9fbe7

Browse files
bonzinigregkh
authored andcommitted
mm: provide a saner PTE walking API for modules
commit 9fd6dad upstream. Currently, the follow_pfn function is exported for modules but follow_pte is not. However, follow_pfn is very easy to misuse, because it does not provide protections (so most of its callers assume the page is writable!) and because it returns after having already unlocked the page table lock. Provide instead a simplified version of follow_pte that does not have the pmdpp and range arguments. The older version survives as follow_invalidate_pte() for use by fs/dax.c. Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 32f070a commit 3f9fbe7

4 files changed

Lines changed: 45 additions & 11 deletions

File tree

fs/dax.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -794,11 +794,12 @@ static void dax_entry_mkclean(struct address_space *mapping, pgoff_t index,
794794
address = pgoff_address(index, vma);
795795

796796
/*
797-
* Note because we provide range to follow_pte it will call
797+
* follow_invalidate_pte() will use the range to call
798798
* mmu_notifier_invalidate_range_start() on our behalf before
799799
* taking any lock.
800800
*/
801-
if (follow_pte(vma->vm_mm, address, &range, &ptep, &pmdp, &ptl))
801+
if (follow_invalidate_pte(vma->vm_mm, address, &range, &ptep,
802+
&pmdp, &ptl))
802803
continue;
803804

804805
/*

include/linux/mm.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,9 +1466,11 @@ void free_pgd_range(struct mmu_gather *tlb, unsigned long addr,
14661466
unsigned long end, unsigned long floor, unsigned long ceiling);
14671467
int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
14681468
struct vm_area_struct *vma);
1469+
int follow_invalidate_pte(struct mm_struct *mm, unsigned long address,
1470+
struct mmu_notifier_range *range, pte_t **ptepp,
1471+
pmd_t **pmdpp, spinlock_t **ptlp);
14691472
int follow_pte(struct mm_struct *mm, unsigned long address,
1470-
struct mmu_notifier_range *range, pte_t **ptepp, pmd_t **pmdpp,
1471-
spinlock_t **ptlp);
1473+
pte_t **ptepp, spinlock_t **ptlp);
14721474
int follow_pfn(struct vm_area_struct *vma, unsigned long address,
14731475
unsigned long *pfn);
14741476
int follow_phys(struct vm_area_struct *vma, unsigned long address,

mm/memory.c

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4222,9 +4222,9 @@ int __pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address)
42224222
}
42234223
#endif /* __PAGETABLE_PMD_FOLDED */
42244224

4225-
int follow_pte(struct mm_struct *mm, unsigned long address,
4226-
struct mmu_notifier_range *range, pte_t **ptepp, pmd_t **pmdpp,
4227-
spinlock_t **ptlp)
4225+
int follow_invalidate_pte(struct mm_struct *mm, unsigned long address,
4226+
struct mmu_notifier_range *range, pte_t **ptepp,
4227+
pmd_t **pmdpp, spinlock_t **ptlp)
42284228
{
42294229
pgd_t *pgd;
42304230
p4d_t *p4d;
@@ -4289,6 +4289,34 @@ int follow_pte(struct mm_struct *mm, unsigned long address,
42894289
return -EINVAL;
42904290
}
42914291

4292+
/**
4293+
* follow_pte - look up PTE at a user virtual address
4294+
* @mm: the mm_struct of the target address space
4295+
* @address: user virtual address
4296+
* @ptepp: location to store found PTE
4297+
* @ptlp: location to store the lock for the PTE
4298+
*
4299+
* On a successful return, the pointer to the PTE is stored in @ptepp;
4300+
* the corresponding lock is taken and its location is stored in @ptlp.
4301+
* The contents of the PTE are only stable until @ptlp is released;
4302+
* any further use, if any, must be protected against invalidation
4303+
* with MMU notifiers.
4304+
*
4305+
* Only IO mappings and raw PFN mappings are allowed. The mmap semaphore
4306+
* should be taken for read.
4307+
*
4308+
* KVM uses this function. While it is arguably less bad than ``follow_pfn``,
4309+
* it is not a good general-purpose API.
4310+
*
4311+
* Return: zero on success, -ve otherwise.
4312+
*/
4313+
int follow_pte(struct mm_struct *mm, unsigned long address,
4314+
pte_t **ptepp, spinlock_t **ptlp)
4315+
{
4316+
return follow_invalidate_pte(mm, address, NULL, ptepp, NULL, ptlp);
4317+
}
4318+
EXPORT_SYMBOL_GPL(follow_pte);
4319+
42924320
/**
42934321
* follow_pfn - look up PFN at a user virtual address
42944322
* @vma: memory mapping
@@ -4297,6 +4325,9 @@ int follow_pte(struct mm_struct *mm, unsigned long address,
42974325
*
42984326
* Only IO mappings and raw PFN mappings are allowed.
42994327
*
4328+
* This function does not allow the caller to read the permissions
4329+
* of the PTE. Do not use it.
4330+
*
43004331
* Return: zero and the pfn at @pfn on success, -ve otherwise.
43014332
*/
43024333
int follow_pfn(struct vm_area_struct *vma, unsigned long address,
@@ -4309,7 +4340,7 @@ int follow_pfn(struct vm_area_struct *vma, unsigned long address,
43094340
if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
43104341
return ret;
43114342

4312-
ret = follow_pte(vma->vm_mm, address, NULL, &ptep, NULL, &ptl);
4343+
ret = follow_pte(vma->vm_mm, address, &ptep, &ptl);
43134344
if (ret)
43144345
return ret;
43154346
*pfn = pte_pfn(*ptep);
@@ -4330,7 +4361,7 @@ int follow_phys(struct vm_area_struct *vma,
43304361
if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
43314362
goto out;
43324363

4333-
if (follow_pte(vma->vm_mm, address, NULL, &ptep, NULL, &ptl))
4364+
if (follow_pte(vma->vm_mm, address, &ptep, &ptl))
43344365
goto out;
43354366
pte = *ptep;
43364367

virt/kvm/kvm_main.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,7 +1603,7 @@ static int hva_to_pfn_remapped(struct vm_area_struct *vma,
16031603
spinlock_t *ptl;
16041604
int r;
16051605

1606-
r = follow_pte(vma->vm_mm, addr, NULL, &ptep, NULL, &ptl);
1606+
r = follow_pte(vma->vm_mm, addr, &ptep, &ptl);
16071607
if (r) {
16081608
/*
16091609
* get_user_pages fails for VM_IO and VM_PFNMAP vmas and does
@@ -1618,7 +1618,7 @@ static int hva_to_pfn_remapped(struct vm_area_struct *vma,
16181618
if (r)
16191619
return r;
16201620

1621-
r = follow_pte(vma->vm_mm, addr, NULL, &ptep, NULL, &ptl);
1621+
r = follow_pte(vma->vm_mm, addr, &ptep, &ptl);
16221622
if (r)
16231623
return r;
16241624
}

0 commit comments

Comments
 (0)