mm/core, x86/mm/pkeys: Differentiate instruction fetches
[linux-2.6-block.git] / arch / x86 / mm / fault.c
index 6e71dcf699ab59f55288ba0923a155e64982df64..d81744e6f39f24d3dc36c50f550cebf977533c9c 100644 (file)
@@ -897,6 +897,23 @@ bad_area(struct pt_regs *regs, unsigned long error_code, unsigned long address)
        __bad_area(regs, error_code, address, NULL, SEGV_MAPERR);
 }
 
+static inline bool bad_area_access_from_pkeys(unsigned long error_code,
+               struct vm_area_struct *vma)
+{
+       /* This code is always called on the current mm */
+       bool foreign = false;
+
+       if (!boot_cpu_has(X86_FEATURE_OSPKE))
+               return false;
+       if (error_code & PF_PK)
+               return true;
+       /* this checks permission keys on the VMA: */
+       if (!arch_vma_access_permitted(vma, (error_code & PF_WRITE),
+                               (error_code & PF_INSTR), foreign))
+               return true;
+       return false;
+}
+
 static noinline void
 bad_area_access_error(struct pt_regs *regs, unsigned long error_code,
                      unsigned long address, struct vm_area_struct *vma)
@@ -906,7 +923,7 @@ bad_area_access_error(struct pt_regs *regs, unsigned long error_code,
         * But, doing it this way allows compiler optimizations
         * if pkeys are compiled out.
         */
-       if (boot_cpu_has(X86_FEATURE_OSPKE) && (error_code & PF_PK))
+       if (bad_area_access_from_pkeys(error_code, vma))
                __bad_area(regs, error_code, address, vma, SEGV_PKUERR);
        else
                __bad_area(regs, error_code, address, vma, SEGV_ACCERR);
@@ -1081,6 +1098,25 @@ int show_unhandled_signals = 1;
 static inline int
 access_error(unsigned long error_code, struct vm_area_struct *vma)
 {
+       /* This is only called for the current mm, so: */
+       bool foreign = false;
+       /*
+        * Access or read was blocked by protection keys. We do
+        * this check before any others because we do not want
+        * to, for instance, confuse a protection-key-denied
+        * write with one for which we should do a COW.
+        */
+       if (error_code & PF_PK)
+               return 1;
+       /*
+        * Make sure to check the VMA so that we do not perform
+        * faults just to hit a PF_PK as soon as we fill in a
+        * page.
+        */
+       if (!arch_vma_access_permitted(vma, (error_code & PF_WRITE),
+                               (error_code & PF_INSTR), foreign))
+               return 1;
+
        if (error_code & PF_WRITE) {
                /* write, present and write, not present: */
                if (unlikely(!(vma->vm_flags & VM_WRITE)))
@@ -1233,6 +1269,8 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code,
 
        if (error_code & PF_WRITE)
                flags |= FAULT_FLAG_WRITE;
+       if (error_code & PF_INSTR)
+               flags |= FAULT_FLAG_INSTRUCTION;
 
        /*
         * When running in the kernel we expect faults to occur only to