Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/book3e: Fix set_memory_x() and set_memory_nx()

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

authored by

Christophe Leroy and committed by
Michael Ellerman
b6cb20fd b1b93cb7

+20 -13
+2
arch/powerpc/include/asm/nohash/32/pgtable.h
··· 193 193 } 194 194 #endif 195 195 196 + #ifndef pte_mkexec 196 197 static inline pte_t pte_mkexec(pte_t pte) 197 198 { 198 199 return __pte(pte_val(pte) | _PAGE_EXEC); 199 200 } 201 + #endif 200 202 201 203 #define pmd_none(pmd) (!pmd_val(pmd)) 202 204 #define pmd_bad(pmd) (pmd_val(pmd) & _PMD_BAD)
-5
arch/powerpc/include/asm/nohash/64/pgtable.h
··· 118 118 return __pte(pte_val(pte) & ~_PAGE_RW); 119 119 } 120 120 121 - static inline pte_t pte_mkexec(pte_t pte) 122 - { 123 - return __pte(pte_val(pte) | _PAGE_EXEC); 124 - } 125 - 126 121 #define PMD_BAD_BITS (PTE_TABLE_SIZE-1) 127 122 #define PUD_BAD_BITS (PMD_TABLE_SIZE-1) 128 123
+14 -4
arch/powerpc/include/asm/nohash/pte-book3e.h
··· 48 48 #define _PAGE_WRITETHRU 0x800000 /* W: cache write-through */ 49 49 50 50 /* "Higher level" linux bit combinations */ 51 - #define _PAGE_EXEC _PAGE_BAP_UX /* .. and was cache cleaned */ 51 + #define _PAGE_EXEC (_PAGE_BAP_SX | _PAGE_BAP_UX) /* .. and was cache cleaned */ 52 52 #define _PAGE_RW (_PAGE_BAP_SW | _PAGE_BAP_UW) /* User write permission */ 53 53 #define _PAGE_KERNEL_RW (_PAGE_BAP_SW | _PAGE_BAP_SR | _PAGE_DIRTY) 54 54 #define _PAGE_KERNEL_RO (_PAGE_BAP_SR) ··· 93 93 /* Permission masks used to generate the __P and __S table */ 94 94 #define PAGE_NONE __pgprot(_PAGE_BASE) 95 95 #define PAGE_SHARED __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW) 96 - #define PAGE_SHARED_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW | _PAGE_EXEC) 96 + #define PAGE_SHARED_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW | _PAGE_BAP_UX) 97 97 #define PAGE_COPY __pgprot(_PAGE_BASE | _PAGE_USER) 98 - #define PAGE_COPY_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC) 98 + #define PAGE_COPY_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_BAP_UX) 99 99 #define PAGE_READONLY __pgprot(_PAGE_BASE | _PAGE_USER) 100 - #define PAGE_READONLY_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC) 100 + #define PAGE_READONLY_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_BAP_UX) 101 101 102 102 #ifndef __ASSEMBLY__ 103 103 static inline pte_t pte_mkprivileged(pte_t pte) ··· 113 113 } 114 114 115 115 #define pte_mkuser pte_mkuser 116 + 117 + static inline pte_t pte_mkexec(pte_t pte) 118 + { 119 + if (pte_val(pte) & _PAGE_BAP_UR) 120 + return __pte((pte_val(pte) & ~_PAGE_BAP_SX) | _PAGE_BAP_UX); 121 + else 122 + return __pte((pte_val(pte) & ~_PAGE_BAP_UX) | _PAGE_BAP_SX); 123 + } 124 + #define pte_mkexec pte_mkexec 125 + 116 126 #endif /* __ASSEMBLY__ */ 117 127 118 128 #endif /* __KERNEL__ */
+4 -4
arch/powerpc/mm/nohash/tlb_low_64e.S
··· 222 222 223 223 tlb_miss_fault_bolted: 224 224 /* We need to check if it was an instruction miss */ 225 - andi. r10,r11,_PAGE_EXEC|_PAGE_BAP_SX 225 + andi. r10,r11,_PAGE_BAP_UX|_PAGE_BAP_SX 226 226 bne itlb_miss_fault_bolted 227 227 dtlb_miss_fault_bolted: 228 228 tlb_epilog_bolted ··· 239 239 srdi r15,r16,60 /* get region */ 240 240 bne- itlb_miss_fault_bolted 241 241 242 - li r11,_PAGE_PRESENT|_PAGE_EXEC /* Base perm */ 242 + li r11,_PAGE_PRESENT|_PAGE_BAP_UX /* Base perm */ 243 243 244 244 /* We do the user/kernel test for the PID here along with the RW test 245 245 */ ··· 614 614 615 615 /* We do the user/kernel test for the PID here along with the RW test 616 616 */ 617 - li r11,_PAGE_PRESENT|_PAGE_EXEC /* Base perm */ 617 + li r11,_PAGE_PRESENT|_PAGE_BAP_UX /* Base perm */ 618 618 oris r11,r11,_PAGE_ACCESSED@h 619 619 620 620 cmpldi cr0,r15,0 /* Check for user region */ ··· 734 734 735 735 normal_tlb_miss_access_fault: 736 736 /* We need to check if it was an instruction miss */ 737 - andi. r10,r11,_PAGE_EXEC 737 + andi. r10,r11,_PAGE_BAP_UX 738 738 bne 1f 739 739 ld r14,EX_TLB_DEAR(r12) 740 740 ld r15,EX_TLB_ESR(r12)