aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Hansen <dave.hansen@linux.intel.com>2016-02-12 16:02:24 -0500
committerIngo Molnar <mingo@kernel.org>2016-02-18 13:46:29 -0500
commitd61172b4b695b821388cdb6088a41d431bcbb93b (patch)
tree9e5be0e34ec5e63fd4b728db65a601bd9b904654
parent07f146f53e8de826e4afa3a88ea65bdb13c24959 (diff)
mm/core, x86/mm/pkeys: Differentiate instruction fetches
As discussed earlier, we attempt to enforce protection keys in software. However, the code checks all faults to ensure that they are not violating protection key permissions. It was assumed that all faults are either write faults where we check PKRU[key].WD (write disable) or read faults where we check the AD (access disable) bit. But, there is a third category of faults for protection keys: instruction faults. Instruction faults never run afoul of protection keys because they do not affect instruction fetches. So, plumb the PF_INSTR bit down in to the arch_vma_access_permitted() function where we do the protection key checks. We also add a new FAULT_FLAG_INSTRUCTION. This is because handle_mm_fault() is not passed the architecture-specific error_code where we keep PF_INSTR, so we need to encode the instruction fetch information in to the arch-generic fault flags. Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Dave Hansen <dave@sr71.net> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Rik van Riel <riel@redhat.com> Cc: linux-mm@kvack.org Link: http://lkml.kernel.org/r/20160212210224.96928009@viggo.jf.intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/powerpc/include/asm/mmu_context.h2
-rw-r--r--arch/s390/include/asm/mmu_context.h2
-rw-r--r--arch/x86/include/asm/mmu_context.h5
-rw-r--r--arch/x86/mm/fault.c8
-rw-r--r--include/asm-generic/mm_hooks.h2
-rw-r--r--include/linux/mm.h1
-rw-r--r--mm/gup.c11
-rw-r--r--mm/memory.c1
8 files changed, 24 insertions, 8 deletions
diff --git a/arch/powerpc/include/asm/mmu_context.h b/arch/powerpc/include/asm/mmu_context.h
index df9bf3ed025b..4eaab40e3ade 100644
--- a/arch/powerpc/include/asm/mmu_context.h
+++ b/arch/powerpc/include/asm/mmu_context.h
@@ -149,7 +149,7 @@ static inline void arch_bprm_mm_init(struct mm_struct *mm,
149} 149}
150 150
151static inline bool arch_vma_access_permitted(struct vm_area_struct *vma, 151static inline bool arch_vma_access_permitted(struct vm_area_struct *vma,
152 bool write, bool foreign) 152 bool write, bool execute, bool foreign)
153{ 153{
154 /* by default, allow everything */ 154 /* by default, allow everything */
155 return true; 155 return true;
diff --git a/arch/s390/include/asm/mmu_context.h b/arch/s390/include/asm/mmu_context.h
index 8906600922ce..fa66b6dfa97a 100644
--- a/arch/s390/include/asm/mmu_context.h
+++ b/arch/s390/include/asm/mmu_context.h
@@ -131,7 +131,7 @@ static inline void arch_bprm_mm_init(struct mm_struct *mm,
131} 131}
132 132
133static inline bool arch_vma_access_permitted(struct vm_area_struct *vma, 133static inline bool arch_vma_access_permitted(struct vm_area_struct *vma,
134 bool write, bool foreign) 134 bool write, bool execute, bool foreign)
135{ 135{
136 /* by default, allow everything */ 136 /* by default, allow everything */
137 return true; 137 return true;
diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h
index b4d939a17e60..6572b949cbca 100644
--- a/arch/x86/include/asm/mmu_context.h
+++ b/arch/x86/include/asm/mmu_context.h
@@ -323,8 +323,11 @@ static inline bool vma_is_foreign(struct vm_area_struct *vma)
323} 323}
324 324
325static inline bool arch_vma_access_permitted(struct vm_area_struct *vma, 325static inline bool arch_vma_access_permitted(struct vm_area_struct *vma,
326 bool write, bool foreign) 326 bool write, bool execute, bool foreign)
327{ 327{
328 /* pkeys never affect instruction fetches */
329 if (execute)
330 return true;
328 /* allow access if the VMA is not one from this process */ 331 /* allow access if the VMA is not one from this process */
329 if (foreign || vma_is_foreign(vma)) 332 if (foreign || vma_is_foreign(vma))
330 return true; 333 return true;
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 68ecdffe284e..d81744e6f39f 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -908,7 +908,8 @@ static inline bool bad_area_access_from_pkeys(unsigned long error_code,
908 if (error_code & PF_PK) 908 if (error_code & PF_PK)
909 return true; 909 return true;
910 /* this checks permission keys on the VMA: */ 910 /* this checks permission keys on the VMA: */
911 if (!arch_vma_access_permitted(vma, (error_code & PF_WRITE), foreign)) 911 if (!arch_vma_access_permitted(vma, (error_code & PF_WRITE),
912 (error_code & PF_INSTR), foreign))
912 return true; 913 return true;
913 return false; 914 return false;
914} 915}
@@ -1112,7 +1113,8 @@ access_error(unsigned long error_code, struct vm_area_struct *vma)
1112 * faults just to hit a PF_PK as soon as we fill in a 1113 * faults just to hit a PF_PK as soon as we fill in a
1113 * page. 1114 * page.
1114 */ 1115 */
1115 if (!arch_vma_access_permitted(vma, (error_code & PF_WRITE), foreign)) 1116 if (!arch_vma_access_permitted(vma, (error_code & PF_WRITE),
1117 (error_code & PF_INSTR), foreign))
1116 return 1; 1118 return 1;
1117 1119
1118 if (error_code & PF_WRITE) { 1120 if (error_code & PF_WRITE) {
@@ -1267,6 +1269,8 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code,
1267 1269
1268 if (error_code & PF_WRITE) 1270 if (error_code & PF_WRITE)
1269 flags |= FAULT_FLAG_WRITE; 1271 flags |= FAULT_FLAG_WRITE;
1272 if (error_code & PF_INSTR)
1273 flags |= FAULT_FLAG_INSTRUCTION;
1270 1274
1271 /* 1275 /*
1272 * When running in the kernel we expect faults to occur only to 1276 * When running in the kernel we expect faults to occur only to
diff --git a/include/asm-generic/mm_hooks.h b/include/asm-generic/mm_hooks.h
index d5c9633bd955..cc5d9a1405df 100644
--- a/include/asm-generic/mm_hooks.h
+++ b/include/asm-generic/mm_hooks.h
@@ -27,7 +27,7 @@ static inline void arch_bprm_mm_init(struct mm_struct *mm,
27} 27}
28 28
29static inline bool arch_vma_access_permitted(struct vm_area_struct *vma, 29static inline bool arch_vma_access_permitted(struct vm_area_struct *vma,
30 bool write, bool foreign) 30 bool write, bool execute, bool foreign)
31{ 31{
32 /* by default, allow everything */ 32 /* by default, allow everything */
33 return true; 33 return true;
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 2aaa0f0d67ea..7955c3eb83db 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -252,6 +252,7 @@ extern pgprot_t protection_map[16];
252#define FAULT_FLAG_TRIED 0x20 /* Second try */ 252#define FAULT_FLAG_TRIED 0x20 /* Second try */
253#define FAULT_FLAG_USER 0x40 /* The fault originated in userspace */ 253#define FAULT_FLAG_USER 0x40 /* The fault originated in userspace */
254#define FAULT_FLAG_REMOTE 0x80 /* faulting for non current tsk/mm */ 254#define FAULT_FLAG_REMOTE 0x80 /* faulting for non current tsk/mm */
255#define FAULT_FLAG_INSTRUCTION 0x100 /* The fault was during an instruction fetch */
255 256
256/* 257/*
257 * vm_fault is filled by the the pagefault handler and passed to the vma's 258 * vm_fault is filled by the the pagefault handler and passed to the vma's
diff --git a/mm/gup.c b/mm/gup.c
index d276760163b3..7f1c4fb77cfa 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -449,7 +449,11 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags)
449 if (!(vm_flags & VM_MAYREAD)) 449 if (!(vm_flags & VM_MAYREAD))
450 return -EFAULT; 450 return -EFAULT;
451 } 451 }
452 if (!arch_vma_access_permitted(vma, write, foreign)) 452 /*
453 * gups are always data accesses, not instruction
454 * fetches, so execute=false here
455 */
456 if (!arch_vma_access_permitted(vma, write, false, foreign))
453 return -EFAULT; 457 return -EFAULT;
454 return 0; 458 return 0;
455} 459}
@@ -629,8 +633,11 @@ bool vma_permits_fault(struct vm_area_struct *vma, unsigned int fault_flags)
629 /* 633 /*
630 * The architecture might have a hardware protection 634 * The architecture might have a hardware protection
631 * mechanism other than read/write that can deny access. 635 * mechanism other than read/write that can deny access.
636 *
637 * gup always represents data access, not instruction
638 * fetches, so execute=false here:
632 */ 639 */
633 if (!arch_vma_access_permitted(vma, write, foreign)) 640 if (!arch_vma_access_permitted(vma, write, false, foreign))
634 return false; 641 return false;
635 642
636 return true; 643 return true;
diff --git a/mm/memory.c b/mm/memory.c
index 76c44e5dffa2..99e9f928264a 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3380,6 +3380,7 @@ static int __handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
3380 pte_t *pte; 3380 pte_t *pte;
3381 3381
3382 if (!arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE, 3382 if (!arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE,
3383 flags & FAULT_FLAG_INSTRUCTION,
3383 flags & FAULT_FLAG_REMOTE)) 3384 flags & FAULT_FLAG_REMOTE))
3384 return VM_FAULT_SIGSEGV; 3385 return VM_FAULT_SIGSEGV;
3385 3386