powerpc/book3e: Fix set_memory_x() and set_memory_nx()
authorChristophe Leroy <christophe.leroy@csgroup.eu>
Tue, 26 Oct 2021 05:39:25 +0000 (07:39 +0200)
committerMichael Ellerman <mpe@ellerman.id.au>
Wed, 27 Oct 2021 13:41:29 +0000 (00:41 +1100)
set_memory_x() calls pte_mkexec() which sets _PAGE_EXEC.
set_memory_nx() calls pte_exprotec() which clears _PAGE_EXEC.

Book3e has 2 bits, UX and SX, which defines the exec rights
resp. for user (PR=1) and for kernel (PR=0).

_PAGE_EXEC is defined as UX only.

An executable kernel page is set with either _PAGE_KERNEL_RWX
or _PAGE_KERNEL_ROX, which both have SX set and UX cleared.

So set_memory_nx() call for an executable kernel page does
nothing because UX is already cleared.

And set_memory_x() on a non-executable kernel page makes it
executable for the user and keeps it non-executable for kernel.

Also, pte_exec() always returns 'false' on kernel pages, because
it checks _PAGE_EXEC which doesn't include SX, so for instance
the W+X check doesn't work.

To fix this:
  - change tlb_low_64e.S to use _PAGE_BAP_UX instead of _PAGE_USER
  - sets both UX and SX in _PAGE_EXEC so that pte_exec() returns
    true whenever one of the two bits is set and pte_exprotect()
    clears both bits.
  - Define a book3e specific version of pte_mkexec() which sets
    either SX or UX based on UR.

Fixes: 1f9ad21c3b38 ("powerpc/mm: Implement set_memory() routines")
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/c41100f9c144dc5b62e5a751b810190c6b5d42fd.1635226743.git.christophe.leroy@csgroup.eu
arch/powerpc/include/asm/nohash/32/pgtable.h
arch/powerpc/include/asm/nohash/64/pgtable.h
arch/powerpc/include/asm/nohash/pte-book3e.h
arch/powerpc/mm/nohash/tlb_low_64e.S

index 11c6849f78647debb6319f11eafed2cdc305a938..b67742e2a9b22910e3828e19ddfdd9a2b90d3e76 100644 (file)
@@ -193,10 +193,12 @@ static inline pte_t pte_wrprotect(pte_t pte)
 }
 #endif
 
+#ifndef pte_mkexec
 static inline pte_t pte_mkexec(pte_t pte)
 {
        return __pte(pte_val(pte) | _PAGE_EXEC);
 }
+#endif
 
 #define pmd_none(pmd)          (!pmd_val(pmd))
 #define        pmd_bad(pmd)            (pmd_val(pmd) & _PMD_BAD)
index d081704b13fb914432e82591055f640a6c623d42..9d2905a474103c518d235d2fd35a9ef4a675c382 100644 (file)
@@ -118,11 +118,6 @@ static inline pte_t pte_wrprotect(pte_t pte)
        return __pte(pte_val(pte) & ~_PAGE_RW);
 }
 
-static inline pte_t pte_mkexec(pte_t pte)
-{
-       return __pte(pte_val(pte) | _PAGE_EXEC);
-}
-
 #define PMD_BAD_BITS           (PTE_TABLE_SIZE-1)
 #define PUD_BAD_BITS           (PMD_TABLE_SIZE-1)
 
index 813918f4076534b91494ccc5803158ad25d4f99f..f798640422c2d6831de682e3b5a80a0b9c1ca389 100644 (file)
@@ -48,7 +48,7 @@
 #define _PAGE_WRITETHRU        0x800000 /* W: cache write-through */
 
 /* "Higher level" linux bit combinations */
-#define _PAGE_EXEC             _PAGE_BAP_UX /* .. and was cache cleaned */
+#define _PAGE_EXEC             (_PAGE_BAP_SX | _PAGE_BAP_UX) /* .. and was cache cleaned */
 #define _PAGE_RW               (_PAGE_BAP_SW | _PAGE_BAP_UW) /* User write permission */
 #define _PAGE_KERNEL_RW                (_PAGE_BAP_SW | _PAGE_BAP_SR | _PAGE_DIRTY)
 #define _PAGE_KERNEL_RO                (_PAGE_BAP_SR)
 /* Permission masks used to generate the __P and __S table */
 #define PAGE_NONE      __pgprot(_PAGE_BASE)
 #define PAGE_SHARED    __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW)
-#define PAGE_SHARED_X  __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW | _PAGE_EXEC)
+#define PAGE_SHARED_X  __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW | _PAGE_BAP_UX)
 #define PAGE_COPY      __pgprot(_PAGE_BASE | _PAGE_USER)
-#define PAGE_COPY_X    __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC)
+#define PAGE_COPY_X    __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_BAP_UX)
 #define PAGE_READONLY  __pgprot(_PAGE_BASE | _PAGE_USER)
-#define PAGE_READONLY_X        __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC)
+#define PAGE_READONLY_X        __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_BAP_UX)
 
 #ifndef __ASSEMBLY__
 static inline pte_t pte_mkprivileged(pte_t pte)
@@ -113,6 +113,16 @@ static inline pte_t pte_mkuser(pte_t pte)
 }
 
 #define pte_mkuser pte_mkuser
+
+static inline pte_t pte_mkexec(pte_t pte)
+{
+       if (pte_val(pte) & _PAGE_BAP_UR)
+               return __pte((pte_val(pte) & ~_PAGE_BAP_SX) | _PAGE_BAP_UX);
+       else
+               return __pte((pte_val(pte) & ~_PAGE_BAP_UX) | _PAGE_BAP_SX);
+}
+#define pte_mkexec pte_mkexec
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* __KERNEL__ */
index bf24451f3e71f0678c92bfec62003fcd9ebe7e51..9235e720e3572642b88456cd8b380e1b36e45009 100644 (file)
@@ -222,7 +222,7 @@ tlb_miss_kernel_bolted:
 
 tlb_miss_fault_bolted:
        /* We need to check if it was an instruction miss */
-       andi.   r10,r11,_PAGE_EXEC|_PAGE_BAP_SX
+       andi.   r10,r11,_PAGE_BAP_UX|_PAGE_BAP_SX
        bne     itlb_miss_fault_bolted
 dtlb_miss_fault_bolted:
        tlb_epilog_bolted
@@ -239,7 +239,7 @@ itlb_miss_fault_bolted:
        srdi    r15,r16,60              /* get region */
        bne-    itlb_miss_fault_bolted
 
-       li      r11,_PAGE_PRESENT|_PAGE_EXEC    /* Base perm */
+       li      r11,_PAGE_PRESENT|_PAGE_BAP_UX  /* Base perm */
 
        /* We do the user/kernel test for the PID here along with the RW test
         */
@@ -614,7 +614,7 @@ itlb_miss_fault_e6500:
 
        /* We do the user/kernel test for the PID here along with the RW test
         */
-       li      r11,_PAGE_PRESENT|_PAGE_EXEC    /* Base perm */
+       li      r11,_PAGE_PRESENT|_PAGE_BAP_UX  /* Base perm */
        oris    r11,r11,_PAGE_ACCESSED@h
 
        cmpldi  cr0,r15,0                       /* Check for user region */
@@ -734,7 +734,7 @@ normal_tlb_miss_done:
 
 normal_tlb_miss_access_fault:
        /* We need to check if it was an instruction miss */
-       andi.   r10,r11,_PAGE_EXEC
+       andi.   r10,r11,_PAGE_BAP_UX
        bne     1f
        ld      r14,EX_TLB_DEAR(r12)
        ld      r15,EX_TLB_ESR(r12)