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: Christophe Leroy <christophe.leroy@csgroup.eu> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/894f110397fcd248e125fb855d1e863e4e633a0d.1634627931.git.christophe.leroy@csgroup.eu
This commit is contained in:
parent
ba454f9c8e
commit
c252f3846d
|
@ -13,11 +13,6 @@
|
|||
|
||||
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)
|
||||
{
|
||||
return !IS_ENABLED(CONFIG_PPC_KUEP);
|
||||
|
@ -30,6 +25,11 @@ static __always_inline bool kuep_is_disabled(void)
|
|||
#define KUAP_NONE (~0UL)
|
||||
#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)
|
||||
{
|
||||
mtsr(mfsr(addr) | SR_KS, addr);
|
||||
|
@ -81,9 +81,6 @@ static inline void __kuap_save_and_lock(struct pt_regs *regs)
|
|||
{
|
||||
unsigned long kuap = current->thread.kuap;
|
||||
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
regs->kuap = kuap;
|
||||
if (unlikely(kuap == KUAP_NONE))
|
||||
return;
|
||||
|
@ -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)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
if (unlikely(kuap != KUAP_NONE)) {
|
||||
current->thread.kuap = KUAP_NONE;
|
||||
kuap_lock(kuap, false);
|
||||
|
@ -118,9 +112,6 @@ static inline unsigned long __kuap_get_and_assert_locked(void)
|
|||
{
|
||||
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);
|
||||
|
||||
return kuap;
|
||||
|
@ -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,
|
||||
u32 size, unsigned long dir)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
BUILD_BUG_ON(!__builtin_constant_p(dir));
|
||||
|
||||
if (!(dir & KUAP_WRITE))
|
||||
|
@ -150,9 +138,6 @@ static __always_inline void __prevent_user_access(unsigned long dir)
|
|||
{
|
||||
u32 kuap = current->thread.kuap;
|
||||
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
BUILD_BUG_ON(!__builtin_constant_p(dir));
|
||||
|
||||
if (!(dir & KUAP_WRITE))
|
||||
|
@ -166,9 +151,6 @@ static inline unsigned long __prevent_user_access_return(void)
|
|||
{
|
||||
unsigned long flags = current->thread.kuap;
|
||||
|
||||
if (kuap_is_disabled())
|
||||
return KUAP_NONE;
|
||||
|
||||
if (flags != KUAP_NONE) {
|
||||
current->thread.kuap = KUAP_NONE;
|
||||
kuap_lock(flags, true);
|
||||
|
@ -179,9 +161,6 @@ static inline unsigned long __prevent_user_access_return(void)
|
|||
|
||||
static inline void __restore_user_access(unsigned long flags)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
if (flags != KUAP_NONE) {
|
||||
current->thread.kuap = flags;
|
||||
kuap_unlock(flags, true);
|
||||
|
@ -193,9 +172,6 @@ __bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
|
|||
{
|
||||
unsigned long kuap = regs->kuap;
|
||||
|
||||
if (kuap_is_disabled())
|
||||
return false;
|
||||
|
||||
if (!is_write || kuap == KUAP_ALL)
|
||||
return false;
|
||||
if (kuap == KUAP_NONE)
|
||||
|
|
|
@ -229,6 +229,11 @@ static inline u64 current_thread_iamr(void)
|
|||
|
||||
#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)
|
||||
{
|
||||
bool restore_amr = false, restore_iamr = false;
|
||||
|
@ -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)
|
||||
{
|
||||
if (mmu_has_feature(MMU_FTR_BOOK3S_KUAP)) {
|
||||
if (unlikely(regs->amr != amr)) {
|
||||
if (likely(regs->amr == amr))
|
||||
return;
|
||||
|
||||
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 need to restore IAMR when returning to kernel space.
|
||||
*/
|
||||
}
|
||||
|
||||
static inline unsigned long __kuap_get_and_assert_locked(void)
|
||||
{
|
||||
if (mmu_has_feature(MMU_FTR_BOOK3S_KUAP)) {
|
||||
unsigned long amr = mfspr(SPRN_AMR);
|
||||
|
||||
if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG)) /* kuap_check_amr() */
|
||||
WARN_ON_ONCE(amr != AMR_KUAP_BLOCKED);
|
||||
return amr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
if (!mmu_has_feature(MMU_FTR_BOOK3S_KUAP))
|
||||
return false;
|
||||
/*
|
||||
* For radix this will be a storage protection fault (DSISR_PROTFAULT).
|
||||
* For hash this will be a key fault (DSISR_KEYFAULT)
|
||||
|
|
|
@ -40,6 +40,8 @@ void setup_kuap(bool disabled);
|
|||
#else
|
||||
static inline void setup_kuap(bool disabled) { }
|
||||
|
||||
static __always_inline bool kuap_is_disabled(void) { return true; }
|
||||
|
||||
static inline bool
|
||||
__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) { }
|
|||
static __always_inline bool
|
||||
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);
|
||||
}
|
||||
|
||||
static __always_inline void kuap_assert_locked(void)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
__kuap_assert_locked();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC32
|
||||
static __always_inline void kuap_save_and_lock(struct pt_regs *regs)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
__kuap_save_and_lock(regs);
|
||||
}
|
||||
#endif
|
||||
|
||||
static __always_inline void kuap_kernel_restore(struct pt_regs *regs, unsigned long amr)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
__kuap_kernel_restore(regs, amr);
|
||||
}
|
||||
|
||||
static __always_inline unsigned long kuap_get_and_assert_locked(void)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return 0;
|
||||
|
||||
return __kuap_get_and_assert_locked();
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
unsigned long size, unsigned long dir)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
__allow_user_access(to, from, size, dir);
|
||||
}
|
||||
|
||||
static __always_inline void prevent_user_access(unsigned long dir)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
__prevent_user_access(dir);
|
||||
}
|
||||
|
||||
static __always_inline unsigned long prevent_user_access_return(void)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return 0;
|
||||
|
||||
return __prevent_user_access_return();
|
||||
}
|
||||
|
||||
static __always_inline void restore_user_access(unsigned long flags)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
__restore_user_access(flags);
|
||||
}
|
||||
#endif /* CONFIG_PPC_BOOK3S_64 */
|
||||
|
|
|
@ -22,9 +22,6 @@ static __always_inline bool kuap_is_disabled(void)
|
|||
|
||||
static inline void __kuap_save_and_lock(struct pt_regs *regs)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
regs->kuap = mfspr(SPRN_MD_AP);
|
||||
mtspr(SPRN_MD_AP, MD_APG_KUAP);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
mtspr(SPRN_MD_AP, regs->kuap);
|
||||
}
|
||||
|
||||
|
@ -45,9 +39,6 @@ static inline unsigned long __kuap_get_and_assert_locked(void)
|
|||
{
|
||||
unsigned long kuap;
|
||||
|
||||
if (kuap_is_disabled())
|
||||
return MD_APG_INIT;
|
||||
|
||||
kuap = mfspr(SPRN_MD_AP);
|
||||
|
||||
if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG))
|
||||
|
@ -58,24 +49,18 @@ static inline unsigned long __kuap_get_and_assert_locked(void)
|
|||
|
||||
static inline void __kuap_assert_locked(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG) && !kuap_is_disabled())
|
||||
kuap_get_and_assert_locked();
|
||||
if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG))
|
||||
__kuap_get_and_assert_locked();
|
||||
}
|
||||
|
||||
static inline void __allow_user_access(void __user *to, const void __user *from,
|
||||
unsigned long size, unsigned long dir)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
mtspr(SPRN_MD_AP, MD_APG_INIT);
|
||||
}
|
||||
|
||||
static inline void __prevent_user_access(unsigned long dir)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
mtspr(SPRN_MD_AP, MD_APG_KUAP);
|
||||
}
|
||||
|
||||
|
@ -83,9 +68,6 @@ static inline unsigned long __prevent_user_access_return(void)
|
|||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (kuap_is_disabled())
|
||||
return MD_APG_INIT;
|
||||
|
||||
flags = mfspr(SPRN_MD_AP);
|
||||
|
||||
mtspr(SPRN_MD_AP, MD_APG_KUAP);
|
||||
|
@ -95,18 +77,12 @@ static inline unsigned long __prevent_user_access_return(void)
|
|||
|
||||
static inline void __restore_user_access(unsigned long flags)
|
||||
{
|
||||
if (kuap_is_disabled())
|
||||
return;
|
||||
|
||||
mtspr(SPRN_MD_AP, flags);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
__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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue