Commit c252f384 authored by Christophe Leroy's avatar Christophe Leroy Committed by Michael Ellerman

powerpc/kuap: Check KUAP activation in generic functions

Today, every platform checks that KUAP is not de-activated
before doing the real job.

Move the verification out of platform specific functions.
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/894f110397fcd248e125fb855d1e863e4e633a0d.1634627931.git.christophe.leroy@csgroup.eu
parent ba454f9c
...@@ -13,11 +13,6 @@ ...@@ -13,11 +13,6 @@
extern struct static_key_false disable_kuap_key; extern struct static_key_false disable_kuap_key;
static __always_inline bool kuap_is_disabled(void)
{
return !IS_ENABLED(CONFIG_PPC_KUAP) || static_branch_unlikely(&disable_kuap_key);
}
static __always_inline bool kuep_is_disabled(void) static __always_inline bool kuep_is_disabled(void)
{ {
return !IS_ENABLED(CONFIG_PPC_KUEP); return !IS_ENABLED(CONFIG_PPC_KUEP);
...@@ -30,6 +25,11 @@ static __always_inline bool kuep_is_disabled(void) ...@@ -30,6 +25,11 @@ static __always_inline bool kuep_is_disabled(void)
#define KUAP_NONE (~0UL) #define KUAP_NONE (~0UL)
#define KUAP_ALL (~1UL) #define KUAP_ALL (~1UL)
static __always_inline bool kuap_is_disabled(void)
{
return static_branch_unlikely(&disable_kuap_key);
}
static inline void kuap_lock_one(unsigned long addr) static inline void kuap_lock_one(unsigned long addr)
{ {
mtsr(mfsr(addr) | SR_KS, addr); mtsr(mfsr(addr) | SR_KS, addr);
...@@ -81,9 +81,6 @@ static inline void __kuap_save_and_lock(struct pt_regs *regs) ...@@ -81,9 +81,6 @@ static inline void __kuap_save_and_lock(struct pt_regs *regs)
{ {
unsigned long kuap = current->thread.kuap; unsigned long kuap = current->thread.kuap;
if (kuap_is_disabled())
return;
regs->kuap = kuap; regs->kuap = kuap;
if (unlikely(kuap == KUAP_NONE)) if (unlikely(kuap == KUAP_NONE))
return; return;
...@@ -98,9 +95,6 @@ static inline void kuap_user_restore(struct pt_regs *regs) ...@@ -98,9 +95,6 @@ static inline void kuap_user_restore(struct pt_regs *regs)
static inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long kuap) static inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long kuap)
{ {
if (kuap_is_disabled())
return;
if (unlikely(kuap != KUAP_NONE)) { if (unlikely(kuap != KUAP_NONE)) {
current->thread.kuap = KUAP_NONE; current->thread.kuap = KUAP_NONE;
kuap_lock(kuap, false); kuap_lock(kuap, false);
...@@ -118,9 +112,6 @@ static inline unsigned long __kuap_get_and_assert_locked(void) ...@@ -118,9 +112,6 @@ static inline unsigned long __kuap_get_and_assert_locked(void)
{ {
unsigned long kuap = current->thread.kuap; unsigned long kuap = current->thread.kuap;
if (kuap_is_disabled())
return KUAP_NONE;
WARN_ON_ONCE(IS_ENABLED(CONFIG_PPC_KUAP_DEBUG) && kuap != KUAP_NONE); WARN_ON_ONCE(IS_ENABLED(CONFIG_PPC_KUAP_DEBUG) && kuap != KUAP_NONE);
return kuap; return kuap;
...@@ -134,9 +125,6 @@ static inline void __kuap_assert_locked(void) ...@@ -134,9 +125,6 @@ static inline void __kuap_assert_locked(void)
static __always_inline void __allow_user_access(void __user *to, const void __user *from, static __always_inline void __allow_user_access(void __user *to, const void __user *from,
u32 size, unsigned long dir) u32 size, unsigned long dir)
{ {
if (kuap_is_disabled())
return;
BUILD_BUG_ON(!__builtin_constant_p(dir)); BUILD_BUG_ON(!__builtin_constant_p(dir));
if (!(dir & KUAP_WRITE)) if (!(dir & KUAP_WRITE))
...@@ -150,9 +138,6 @@ static __always_inline void __prevent_user_access(unsigned long dir) ...@@ -150,9 +138,6 @@ static __always_inline void __prevent_user_access(unsigned long dir)
{ {
u32 kuap = current->thread.kuap; u32 kuap = current->thread.kuap;
if (kuap_is_disabled())
return;
BUILD_BUG_ON(!__builtin_constant_p(dir)); BUILD_BUG_ON(!__builtin_constant_p(dir));
if (!(dir & KUAP_WRITE)) if (!(dir & KUAP_WRITE))
...@@ -166,9 +151,6 @@ static inline unsigned long __prevent_user_access_return(void) ...@@ -166,9 +151,6 @@ static inline unsigned long __prevent_user_access_return(void)
{ {
unsigned long flags = current->thread.kuap; unsigned long flags = current->thread.kuap;
if (kuap_is_disabled())
return KUAP_NONE;
if (flags != KUAP_NONE) { if (flags != KUAP_NONE) {
current->thread.kuap = KUAP_NONE; current->thread.kuap = KUAP_NONE;
kuap_lock(flags, true); kuap_lock(flags, true);
...@@ -179,9 +161,6 @@ static inline unsigned long __prevent_user_access_return(void) ...@@ -179,9 +161,6 @@ static inline unsigned long __prevent_user_access_return(void)
static inline void __restore_user_access(unsigned long flags) static inline void __restore_user_access(unsigned long flags)
{ {
if (kuap_is_disabled())
return;
if (flags != KUAP_NONE) { if (flags != KUAP_NONE) {
current->thread.kuap = flags; current->thread.kuap = flags;
kuap_unlock(flags, true); kuap_unlock(flags, true);
...@@ -193,9 +172,6 @@ __bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write) ...@@ -193,9 +172,6 @@ __bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
{ {
unsigned long kuap = regs->kuap; unsigned long kuap = regs->kuap;
if (kuap_is_disabled())
return false;
if (!is_write || kuap == KUAP_ALL) if (!is_write || kuap == KUAP_ALL)
return false; return false;
if (kuap == KUAP_NONE) if (kuap == KUAP_NONE)
......
...@@ -229,6 +229,11 @@ static inline u64 current_thread_iamr(void) ...@@ -229,6 +229,11 @@ static inline u64 current_thread_iamr(void)
#ifdef CONFIG_PPC_KUAP #ifdef CONFIG_PPC_KUAP
static __always_inline bool kuap_is_disabled(void)
{
return !mmu_has_feature(MMU_FTR_BOOK3S_KUAP);
}
static inline void kuap_user_restore(struct pt_regs *regs) static inline void kuap_user_restore(struct pt_regs *regs)
{ {
bool restore_amr = false, restore_iamr = false; bool restore_amr = false, restore_iamr = false;
...@@ -270,36 +275,32 @@ static inline void kuap_user_restore(struct pt_regs *regs) ...@@ -270,36 +275,32 @@ static inline void kuap_user_restore(struct pt_regs *regs)
static inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long amr) static inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long amr)
{ {
if (mmu_has_feature(MMU_FTR_BOOK3S_KUAP)) { if (likely(regs->amr == amr))
if (unlikely(regs->amr != amr)) { return;
isync();
mtspr(SPRN_AMR, regs->amr); isync();
/* mtspr(SPRN_AMR, regs->amr);
* No isync required here because we are about to rfi
* back to previous context before any user accesses
* would be made, which is a CSI.
*/
}
}
/* /*
* No isync required here because we are about to rfi
* back to previous context before any user accesses
* would be made, which is a CSI.
*
* No need to restore IAMR when returning to kernel space. * No need to restore IAMR when returning to kernel space.
*/ */
} }
static inline unsigned long __kuap_get_and_assert_locked(void) static inline unsigned long __kuap_get_and_assert_locked(void)
{ {
if (mmu_has_feature(MMU_FTR_BOOK3S_KUAP)) { unsigned long amr = mfspr(SPRN_AMR);
unsigned long amr = mfspr(SPRN_AMR);
if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG)) /* kuap_check_amr() */ if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG)) /* kuap_check_amr() */
WARN_ON_ONCE(amr != AMR_KUAP_BLOCKED); WARN_ON_ONCE(amr != AMR_KUAP_BLOCKED);
return amr; return amr;
}
return 0;
} }
static inline void __kuap_assert_locked(void) static inline void __kuap_assert_locked(void)
{ {
if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG) && mmu_has_feature(MMU_FTR_BOOK3S_KUAP)) if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG))
WARN_ON_ONCE(mfspr(SPRN_AMR) != AMR_KUAP_BLOCKED); WARN_ON_ONCE(mfspr(SPRN_AMR) != AMR_KUAP_BLOCKED);
} }
...@@ -340,8 +341,6 @@ static inline void set_kuap(unsigned long value) ...@@ -340,8 +341,6 @@ static inline void set_kuap(unsigned long value)
static inline bool __bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write) static inline bool __bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
{ {
if (!mmu_has_feature(MMU_FTR_BOOK3S_KUAP))
return false;
/* /*
* For radix this will be a storage protection fault (DSISR_PROTFAULT). * For radix this will be a storage protection fault (DSISR_PROTFAULT).
* For hash this will be a key fault (DSISR_KEYFAULT) * For hash this will be a key fault (DSISR_KEYFAULT)
......
...@@ -40,6 +40,8 @@ void setup_kuap(bool disabled); ...@@ -40,6 +40,8 @@ void setup_kuap(bool disabled);
#else #else
static inline void setup_kuap(bool disabled) { } static inline void setup_kuap(bool disabled) { }
static __always_inline bool kuap_is_disabled(void) { return true; }
static inline bool static inline bool
__bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write) __bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
{ {
...@@ -73,28 +75,43 @@ static inline void __restore_user_access(unsigned long flags) { } ...@@ -73,28 +75,43 @@ static inline void __restore_user_access(unsigned long flags) { }
static __always_inline bool static __always_inline bool
bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write) bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
{ {
if (kuap_is_disabled())
return false;
return __bad_kuap_fault(regs, address, is_write); return __bad_kuap_fault(regs, address, is_write);
} }
static __always_inline void kuap_assert_locked(void) static __always_inline void kuap_assert_locked(void)
{ {
if (kuap_is_disabled())
return;
__kuap_assert_locked(); __kuap_assert_locked();
} }
#ifdef CONFIG_PPC32 #ifdef CONFIG_PPC32
static __always_inline void kuap_save_and_lock(struct pt_regs *regs) static __always_inline void kuap_save_and_lock(struct pt_regs *regs)
{ {
if (kuap_is_disabled())
return;
__kuap_save_and_lock(regs); __kuap_save_and_lock(regs);
} }
#endif #endif
static __always_inline void kuap_kernel_restore(struct pt_regs *regs, unsigned long amr) static __always_inline void kuap_kernel_restore(struct pt_regs *regs, unsigned long amr)
{ {
if (kuap_is_disabled())
return;
__kuap_kernel_restore(regs, amr); __kuap_kernel_restore(regs, amr);
} }
static __always_inline unsigned long kuap_get_and_assert_locked(void) static __always_inline unsigned long kuap_get_and_assert_locked(void)
{ {
if (kuap_is_disabled())
return 0;
return __kuap_get_and_assert_locked(); return __kuap_get_and_assert_locked();
} }
...@@ -102,21 +119,33 @@ static __always_inline unsigned long kuap_get_and_assert_locked(void) ...@@ -102,21 +119,33 @@ static __always_inline unsigned long kuap_get_and_assert_locked(void)
static __always_inline void allow_user_access(void __user *to, const void __user *from, static __always_inline void allow_user_access(void __user *to, const void __user *from,
unsigned long size, unsigned long dir) unsigned long size, unsigned long dir)
{ {
if (kuap_is_disabled())
return;
__allow_user_access(to, from, size, dir); __allow_user_access(to, from, size, dir);
} }
static __always_inline void prevent_user_access(unsigned long dir) static __always_inline void prevent_user_access(unsigned long dir)
{ {
if (kuap_is_disabled())
return;
__prevent_user_access(dir); __prevent_user_access(dir);
} }
static __always_inline unsigned long prevent_user_access_return(void) static __always_inline unsigned long prevent_user_access_return(void)
{ {
if (kuap_is_disabled())
return 0;
return __prevent_user_access_return(); return __prevent_user_access_return();
} }
static __always_inline void restore_user_access(unsigned long flags) static __always_inline void restore_user_access(unsigned long flags)
{ {
if (kuap_is_disabled())
return;
__restore_user_access(flags); __restore_user_access(flags);
} }
#endif /* CONFIG_PPC_BOOK3S_64 */ #endif /* CONFIG_PPC_BOOK3S_64 */
......
...@@ -22,9 +22,6 @@ static __always_inline bool kuap_is_disabled(void) ...@@ -22,9 +22,6 @@ static __always_inline bool kuap_is_disabled(void)
static inline void __kuap_save_and_lock(struct pt_regs *regs) static inline void __kuap_save_and_lock(struct pt_regs *regs)
{ {
if (kuap_is_disabled())
return;
regs->kuap = mfspr(SPRN_MD_AP); regs->kuap = mfspr(SPRN_MD_AP);
mtspr(SPRN_MD_AP, MD_APG_KUAP); mtspr(SPRN_MD_AP, MD_APG_KUAP);
} }
...@@ -35,9 +32,6 @@ static inline void kuap_user_restore(struct pt_regs *regs) ...@@ -35,9 +32,6 @@ static inline void kuap_user_restore(struct pt_regs *regs)
static inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long kuap) static inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long kuap)
{ {
if (kuap_is_disabled())
return;
mtspr(SPRN_MD_AP, regs->kuap); mtspr(SPRN_MD_AP, regs->kuap);
} }
...@@ -45,9 +39,6 @@ static inline unsigned long __kuap_get_and_assert_locked(void) ...@@ -45,9 +39,6 @@ static inline unsigned long __kuap_get_and_assert_locked(void)
{ {
unsigned long kuap; unsigned long kuap;
if (kuap_is_disabled())
return MD_APG_INIT;
kuap = mfspr(SPRN_MD_AP); kuap = mfspr(SPRN_MD_AP);
if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG)) if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG))
...@@ -58,24 +49,18 @@ static inline unsigned long __kuap_get_and_assert_locked(void) ...@@ -58,24 +49,18 @@ static inline unsigned long __kuap_get_and_assert_locked(void)
static inline void __kuap_assert_locked(void) static inline void __kuap_assert_locked(void)
{ {
if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG) && !kuap_is_disabled()) if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG))
kuap_get_and_assert_locked(); __kuap_get_and_assert_locked();
} }
static inline void __allow_user_access(void __user *to, const void __user *from, static inline void __allow_user_access(void __user *to, const void __user *from,
unsigned long size, unsigned long dir) unsigned long size, unsigned long dir)
{ {
if (kuap_is_disabled())
return;
mtspr(SPRN_MD_AP, MD_APG_INIT); mtspr(SPRN_MD_AP, MD_APG_INIT);
} }
static inline void __prevent_user_access(unsigned long dir) static inline void __prevent_user_access(unsigned long dir)
{ {
if (kuap_is_disabled())
return;
mtspr(SPRN_MD_AP, MD_APG_KUAP); mtspr(SPRN_MD_AP, MD_APG_KUAP);
} }
...@@ -83,9 +68,6 @@ static inline unsigned long __prevent_user_access_return(void) ...@@ -83,9 +68,6 @@ static inline unsigned long __prevent_user_access_return(void)
{ {
unsigned long flags; unsigned long flags;
if (kuap_is_disabled())
return MD_APG_INIT;
flags = mfspr(SPRN_MD_AP); flags = mfspr(SPRN_MD_AP);
mtspr(SPRN_MD_AP, MD_APG_KUAP); mtspr(SPRN_MD_AP, MD_APG_KUAP);
...@@ -95,18 +77,12 @@ static inline unsigned long __prevent_user_access_return(void) ...@@ -95,18 +77,12 @@ static inline unsigned long __prevent_user_access_return(void)
static inline void __restore_user_access(unsigned long flags) static inline void __restore_user_access(unsigned long flags)
{ {
if (kuap_is_disabled())
return;
mtspr(SPRN_MD_AP, flags); mtspr(SPRN_MD_AP, flags);
} }
static inline bool static inline bool
__bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write) __bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
{ {
if (kuap_is_disabled())
return false;
return !((regs->kuap ^ MD_APG_KUAP) & 0xff000000); return !((regs->kuap ^ MD_APG_KUAP) & 0xff000000);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment