|
@@ -897,6 +897,16 @@ 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)
|
|
|
+{
|
|
|
+ if (!boot_cpu_has(X86_FEATURE_OSPKE))
|
|
|
+ return false;
|
|
|
+ if (error_code & PF_PK)
|
|
|
+ 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 +916,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 +1091,15 @@ int show_unhandled_signals = 1;
|
|
|
static inline int
|
|
|
access_error(unsigned long error_code, struct vm_area_struct *vma)
|
|
|
{
|
|
|
+ /*
|
|
|
+ * 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;
|
|
|
+
|
|
|
if (error_code & PF_WRITE) {
|
|
|
/* write, present and write, not present: */
|
|
|
if (unlikely(!(vma->vm_flags & VM_WRITE)))
|