mm/gup, x86/mm/pkeys: Check VMAs and PTEs for protection keys
[cascardo/linux.git] / mm / gup.c
index 7bf19ff..e0f5f35 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -1,3 +1,4 @@
+#define __DISABLE_GUP_DEPRECATED 1
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/err.h>
@@ -14,6 +15,7 @@
 #include <linux/rwsem.h>
 #include <linux/hugetlb.h>
 
+#include <asm/mmu_context.h>
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
 
@@ -443,6 +445,8 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags)
                if (!(vm_flags & VM_MAYREAD))
                        return -EFAULT;
        }
+       if (!arch_vma_access_permitted(vma, (gup_flags & FOLL_WRITE)))
+               return -EFAULT;
        return 0;
 }
 
@@ -609,6 +613,24 @@ next_page:
 }
 EXPORT_SYMBOL(__get_user_pages);
 
+bool vma_permits_fault(struct vm_area_struct *vma, unsigned int fault_flags)
+{
+       bool write = !!(fault_flags & FAULT_FLAG_WRITE);
+       vm_flags_t vm_flags = write ? VM_WRITE : VM_READ;
+
+       if (!(vm_flags & vma->vm_flags))
+               return false;
+
+       /*
+        * The architecture might have a hardware protection
+        * mechanism other than read/write that can deny access
+        */
+       if (!arch_vma_access_permitted(vma, write))
+               return false;
+
+       return true;
+}
+
 /*
  * fixup_user_fault() - manually resolve a user page fault
  * @tsk:       the task_struct to use for page fault accounting, or
@@ -644,7 +666,6 @@ int fixup_user_fault(struct task_struct *tsk, struct mm_struct *mm,
                     bool *unlocked)
 {
        struct vm_area_struct *vma;
-       vm_flags_t vm_flags;
        int ret, major = 0;
 
        if (unlocked)
@@ -655,8 +676,7 @@ retry:
        if (!vma || address < vma->vm_start)
                return -EFAULT;
 
-       vm_flags = (fault_flags & FAULT_FLAG_WRITE) ? VM_WRITE : VM_READ;
-       if (!(vm_flags & vma->vm_flags))
+       if (!vma_permits_fault(vma, fault_flags))
                return -EFAULT;
 
        ret = handle_mm_fault(mm, vma, address, fault_flags);
@@ -807,15 +827,15 @@ static __always_inline long __get_user_pages_locked(struct task_struct *tsk,
  *      if (locked)
  *          up_read(&mm->mmap_sem);
  */
-long get_user_pages_locked(struct task_struct *tsk, struct mm_struct *mm,
-                          unsigned long start, unsigned long nr_pages,
+long get_user_pages_locked6(unsigned long start, unsigned long nr_pages,
                           int write, int force, struct page **pages,
                           int *locked)
 {
-       return __get_user_pages_locked(tsk, mm, start, nr_pages, write, force,
-                                      pages, NULL, locked, true, FOLL_TOUCH);
+       return __get_user_pages_locked(current, current->mm, start, nr_pages,
+                                      write, force, pages, NULL, locked, true,
+                                      FOLL_TOUCH);
 }
-EXPORT_SYMBOL(get_user_pages_locked);
+EXPORT_SYMBOL(get_user_pages_locked6);
 
 /*
  * Same as get_user_pages_unlocked(...., FOLL_TOUCH) but it allows to
@@ -860,17 +880,16 @@ EXPORT_SYMBOL(__get_user_pages_unlocked);
  * or if "force" shall be set to 1 (get_user_pages_fast misses the
  * "force" parameter).
  */
-long get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
-                            unsigned long start, unsigned long nr_pages,
+long get_user_pages_unlocked5(unsigned long start, unsigned long nr_pages,
                             int write, int force, struct page **pages)
 {
-       return __get_user_pages_unlocked(tsk, mm, start, nr_pages, write,
-                                        force, pages, FOLL_TOUCH);
+       return __get_user_pages_unlocked(current, current->mm, start, nr_pages,
+                                        write, force, pages, FOLL_TOUCH);
 }
-EXPORT_SYMBOL(get_user_pages_unlocked);
+EXPORT_SYMBOL(get_user_pages_unlocked5);
 
 /*
- * get_user_pages() - pin user pages in memory
+ * get_user_pages_remote() - pin user pages in memory
  * @tsk:       the task_struct to use for page fault accounting, or
  *             NULL if faults are not to be recorded.
  * @mm:                mm_struct of target mm
@@ -924,14 +943,32 @@ EXPORT_SYMBOL(get_user_pages_unlocked);
  * should use get_user_pages because it cannot pass
  * FAULT_FLAG_ALLOW_RETRY to handle_mm_fault.
  */
-long get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
-               unsigned long start, unsigned long nr_pages, int write,
-               int force, struct page **pages, struct vm_area_struct **vmas)
+long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
+               unsigned long start, unsigned long nr_pages,
+               int write, int force, struct page **pages,
+               struct vm_area_struct **vmas)
 {
        return __get_user_pages_locked(tsk, mm, start, nr_pages, write, force,
-                                      pages, vmas, NULL, false, FOLL_TOUCH);
+                                      pages, vmas, NULL, false,
+                                      FOLL_TOUCH | FOLL_REMOTE);
 }
-EXPORT_SYMBOL(get_user_pages);
+EXPORT_SYMBOL(get_user_pages_remote);
+
+/*
+ * This is the same as get_user_pages_remote(), just with a
+ * less-flexible calling convention where we assume that the task
+ * and mm being operated on are the current task's.  We also
+ * obviously don't pass FOLL_REMOTE in here.
+ */
+long get_user_pages6(unsigned long start, unsigned long nr_pages,
+               int write, int force, struct page **pages,
+               struct vm_area_struct **vmas)
+{
+       return __get_user_pages_locked(current, current->mm, start, nr_pages,
+                                      write, force, pages, vmas, NULL, false,
+                                      FOLL_TOUCH);
+}
+EXPORT_SYMBOL(get_user_pages6);
 
 /**
  * populate_vma_page_range() -  populate a range of pages in the vma.
@@ -1144,6 +1181,9 @@ static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
                        pte_protnone(pte) || (write && !pte_write(pte)))
                        goto pte_unmap;
 
+               if (!arch_pte_access_permitted(pte, write))
+                       goto pte_unmap;
+
                VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
                page = pte_page(pte);
                head = compound_head(page);
@@ -1467,3 +1507,38 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write,
 }
 
 #endif /* CONFIG_HAVE_GENERIC_RCU_GUP */
+
+long get_user_pages8(struct task_struct *tsk, struct mm_struct *mm,
+                    unsigned long start, unsigned long nr_pages,
+                    int write, int force, struct page **pages,
+                    struct vm_area_struct **vmas)
+{
+       WARN_ONCE(tsk != current, "get_user_pages() called on remote task");
+       WARN_ONCE(mm != current->mm, "get_user_pages() called on remote mm");
+
+       return get_user_pages6(start, nr_pages, write, force, pages, vmas);
+}
+EXPORT_SYMBOL(get_user_pages8);
+
+long get_user_pages_locked8(struct task_struct *tsk, struct mm_struct *mm,
+                           unsigned long start, unsigned long nr_pages,
+                           int write, int force, struct page **pages, int *locked)
+{
+       WARN_ONCE(tsk != current, "get_user_pages_locked() called on remote task");
+       WARN_ONCE(mm != current->mm, "get_user_pages_locked() called on remote mm");
+
+       return get_user_pages_locked6(start, nr_pages, write, force, pages, locked);
+}
+EXPORT_SYMBOL(get_user_pages_locked8);
+
+long get_user_pages_unlocked7(struct task_struct *tsk, struct mm_struct *mm,
+                                 unsigned long start, unsigned long nr_pages,
+                                 int write, int force, struct page **pages)
+{
+       WARN_ONCE(tsk != current, "get_user_pages_unlocked() called on remote task");
+       WARN_ONCE(mm != current->mm, "get_user_pages_unlocked() called on remote mm");
+
+       return get_user_pages_unlocked5(start, nr_pages, write, force, pages);
+}
+EXPORT_SYMBOL(get_user_pages_unlocked7);
+