Commit b6cb20fd authored by Christophe Leroy's avatar Christophe Leroy Committed by Michael Ellerman
Browse files

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: 1f9ad21c ("powerpc/mm: Implement set_memory() routines")
Signed-off-by: default avatarChristophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/c41100f9c144dc5b62e5a751b810190c6b5d42fd.1635226743.git.christophe.leroy@csgroup.eu
parent b1b93cb7
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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)
+0 −5
Original line number Diff line number Diff line
@@ -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)

+14 −4
Original line number Diff line number Diff line
@@ -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)
@@ -93,11 +93,11 @@
/* 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__ */
+4 −4
Original line number Diff line number Diff line
@@ -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)