ARM: 9131/1: mm: Fix PXN process with LPAE feature
authorWang Kefeng <wangkefeng.wang@huawei.com>
Wed, 22 Sep 2021 13:56:32 +0000 (14:56 +0100)
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Tue, 19 Oct 2021 09:35:43 +0000 (10:35 +0100)
When user code execution with privilege mode, it will lead to
infinite loop in the page fault handler if ARM_LPAE enabled,

The issue could be reproduced with
  "echo EXEC_USERSPACE > /sys/kernel/debug/provoke-crash/DIRECT"

As Permission fault shows in ARM spec,
  IFSR format when using the Short-descriptor translation table format
    Permission fault:       01101 First level      01111 Second level
  IFSR format when using the Long-descriptor translation table format
    Permission fault:       0011LL LL bits indicate levelb.

Add is_permission_fault() function to check permission fault and die
if permission fault occurred under instruction fault in do_page_fault().

Fixes: 1d4d37159d01 ("ARM: 8235/1: Support for the PXN CPU feature on ARMv7")
Signed-off-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
arch/arm/mm/fault.c
arch/arm/mm/fault.h

index 82bcfe5..bc8779d 100644 (file)
@@ -194,6 +194,19 @@ void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 #define VM_FAULT_BADMAP                0x010000
 #define VM_FAULT_BADACCESS     0x020000
 
+static inline bool is_permission_fault(unsigned int fsr)
+{
+       int fs = fsr_fs(fsr);
+#ifdef CONFIG_ARM_LPAE
+       if ((fs & FS_PERM_NOLL_MASK) == FS_PERM_NOLL)
+               return true;
+#else
+       if (fs == FS_L1_PERM || fs == FS_L2_PERM)
+               return true;
+#endif
+       return false;
+}
+
 static vm_fault_t __kprobes
 __do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int flags,
                unsigned long vma_flags, struct pt_regs *regs)
@@ -253,9 +266,14 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
                vm_flags = VM_WRITE;
        }
 
-       if (fsr & FSR_LNX_PF)
+       if (fsr & FSR_LNX_PF) {
                vm_flags = VM_EXEC;
 
+               if (is_permission_fault(fsr) && !user_mode(regs))
+                       die_kernel_fault("execution of memory",
+                                        mm, addr, fsr, regs);
+       }
+
        perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
 
        /*
index 9ecc209..83b5ab3 100644 (file)
@@ -14,6 +14,8 @@
 
 #ifdef CONFIG_ARM_LPAE
 #define FSR_FS_AEA             17
+#define FS_PERM_NOLL           0xC
+#define FS_PERM_NOLL_MASK      0x3C
 
 static inline int fsr_fs(unsigned int fsr)
 {
@@ -21,6 +23,8 @@ static inline int fsr_fs(unsigned int fsr)
 }
 #else
 #define FSR_FS_AEA             22
+#define FS_L1_PERM             0xD
+#define FS_L2_PERM             0xF
 
 static inline int fsr_fs(unsigned int fsr)
 {