mm: provide a saner PTE walking API for modules
authorPaolo Bonzini <pbonzini@redhat.com>
Fri, 5 Feb 2021 10:07:11 +0000 (05:07 -0500)
committerPaolo Bonzini <pbonzini@redhat.com>
Tue, 9 Feb 2021 12:05:44 +0000 (07:05 -0500)
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>
arch/s390/pci/pci_mmio.c
fs/dax.c
include/linux/mm.h
mm/memory.c
virt/kvm/kvm_main.c

index 18f2d10c31764afb802f0ed51102a791f9be227e..474617b886487f017a78e4e9801b938dfb84a81a 100644 (file)
@@ -170,7 +170,7 @@ SYSCALL_DEFINE3(s390_pci_mmio_write, unsigned long, mmio_addr,
        if (!(vma->vm_flags & VM_WRITE))
                goto out_unlock_mmap;
 
-       ret = follow_pte(vma->vm_mm, mmio_addr, NULL, &ptep, NULL, &ptl);
+       ret = follow_pte(vma->vm_mm, mmio_addr, &ptep, &ptl);
        if (ret)
                goto out_unlock_mmap;
 
@@ -311,7 +311,7 @@ SYSCALL_DEFINE3(s390_pci_mmio_read, unsigned long, mmio_addr,
        if (!(vma->vm_flags & VM_WRITE))
                goto out_unlock_mmap;
 
-       ret = follow_pte(vma->vm_mm, mmio_addr, NULL, &ptep, NULL, &ptl);
+       ret = follow_pte(vma->vm_mm, mmio_addr, &ptep, &ptl);
        if (ret)
                goto out_unlock_mmap;
 
index 26d5dcd2d69e5c0afd2c93d7042b54c3324b7342..b3d27fdc67752df52637bccdbde008c0086aa312 100644 (file)
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -810,11 +810,12 @@ static void dax_entry_mkclean(struct address_space *mapping, pgoff_t index,
                address = pgoff_address(index, vma);
 
                /*
-                * Note because we provide range to follow_pte it will call
+                * follow_invalidate_pte() will use the range to call
                 * mmu_notifier_invalidate_range_start() on our behalf before
                 * taking any lock.
                 */
-               if (follow_pte(vma->vm_mm, address, &range, &ptep, &pmdp, &ptl))
+               if (follow_invalidate_pte(vma->vm_mm, address, &range, &ptep,
+                                         &pmdp, &ptl))
                        continue;
 
                /*
index ecdf8a8cd6aebe62f49ae45bf7aab623b9e5282b..24b292fce8e590a933c828a568383fd40bbaebfb 100644 (file)
@@ -1658,9 +1658,11 @@ void free_pgd_range(struct mmu_gather *tlb, unsigned long addr,
                unsigned long end, unsigned long floor, unsigned long ceiling);
 int
 copy_page_range(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma);
+int follow_invalidate_pte(struct mm_struct *mm, unsigned long address,
+                         struct mmu_notifier_range *range, pte_t **ptepp,
+                         pmd_t **pmdpp, spinlock_t **ptlp);
 int follow_pte(struct mm_struct *mm, unsigned long address,
-               struct mmu_notifier_range *range, pte_t **ptepp, pmd_t **pmdpp,
-               spinlock_t **ptlp);
+              pte_t **ptepp, spinlock_t **ptlp);
 int follow_pfn(struct vm_area_struct *vma, unsigned long address,
        unsigned long *pfn);
 int follow_phys(struct vm_area_struct *vma, unsigned long address,
index feff48e1465a6ee652d130560617e2425109ee2f..985dac0958dcfe964fc2a3bd8c72b6abcac95ac5 100644 (file)
@@ -4709,9 +4709,9 @@ int __pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address)
 }
 #endif /* __PAGETABLE_PMD_FOLDED */
 
-int follow_pte(struct mm_struct *mm, unsigned long address,
-              struct mmu_notifier_range *range, pte_t **ptepp, pmd_t **pmdpp,
-              spinlock_t **ptlp)
+int follow_invalidate_pte(struct mm_struct *mm, unsigned long address,
+                         struct mmu_notifier_range *range, pte_t **ptepp,
+                         pmd_t **pmdpp, spinlock_t **ptlp)
 {
        pgd_t *pgd;
        p4d_t *p4d;
@@ -4776,6 +4776,34 @@ out:
        return -EINVAL;
 }
 
+/**
+ * follow_pte - look up PTE at a user virtual address
+ * @mm: the mm_struct of the target address space
+ * @address: user virtual address
+ * @ptepp: location to store found PTE
+ * @ptlp: location to store the lock for the PTE
+ *
+ * On a successful return, the pointer to the PTE is stored in @ptepp;
+ * the corresponding lock is taken and its location is stored in @ptlp.
+ * The contents of the PTE are only stable until @ptlp is released;
+ * any further use, if any, must be protected against invalidation
+ * with MMU notifiers.
+ *
+ * Only IO mappings and raw PFN mappings are allowed.  The mmap semaphore
+ * should be taken for read.
+ *
+ * KVM uses this function.  While it is arguably less bad than ``follow_pfn``,
+ * it is not a good general-purpose API.
+ *
+ * Return: zero on success, -ve otherwise.
+ */
+int follow_pte(struct mm_struct *mm, unsigned long address,
+              pte_t **ptepp, spinlock_t **ptlp)
+{
+       return follow_invalidate_pte(mm, address, NULL, ptepp, NULL, ptlp);
+}
+EXPORT_SYMBOL_GPL(follow_pte);
+
 /**
  * follow_pfn - look up PFN at a user virtual address
  * @vma: memory mapping
@@ -4784,6 +4812,9 @@ out:
  *
  * Only IO mappings and raw PFN mappings are allowed.
  *
+ * This function does not allow the caller to read the permissions
+ * of the PTE.  Do not use it.
+ *
  * Return: zero and the pfn at @pfn on success, -ve otherwise.
  */
 int follow_pfn(struct vm_area_struct *vma, unsigned long address,
@@ -4796,7 +4827,7 @@ int follow_pfn(struct vm_area_struct *vma, unsigned long address,
        if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
                return ret;
 
-       ret = follow_pte(vma->vm_mm, address, NULL, &ptep, NULL, &ptl);
+       ret = follow_pte(vma->vm_mm, address, &ptep, &ptl);
        if (ret)
                return ret;
        *pfn = pte_pfn(*ptep);
@@ -4817,7 +4848,7 @@ int follow_phys(struct vm_area_struct *vma,
        if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
                goto out;
 
-       if (follow_pte(vma->vm_mm, address, NULL, &ptep, NULL, &ptl))
+       if (follow_pte(vma->vm_mm, address, &ptep, &ptl))
                goto out;
        pte = *ptep;
 
index 48ccdf4e3d0431cf7d0f9fb1a6c7ad1ea0a4ed94..ee4ac2618ec59ec9ccbd55db420216a06d826bf8 100644 (file)
@@ -1911,7 +1911,7 @@ static int hva_to_pfn_remapped(struct vm_area_struct *vma,
        spinlock_t *ptl;
        int r;
 
-       r = follow_pte(vma->vm_mm, addr, NULL, &ptep, NULL, &ptl);
+       r = follow_pte(vma->vm_mm, addr, &ptep, &ptl);
        if (r) {
                /*
                 * get_user_pages fails for VM_IO and VM_PFNMAP vmas and does
@@ -1926,7 +1926,7 @@ static int hva_to_pfn_remapped(struct vm_area_struct *vma,
                if (r)
                        return r;
 
-               r = follow_pte(vma->vm_mm, addr, NULL, &ptep, NULL, &ptl);
+               r = follow_pte(vma->vm_mm, addr, &ptep, &ptl);
                if (r)
                        return r;
        }