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

powerpc: Implement user_access_save() and user_access_restore()



Implement user_access_save() and user_access_restore()

On 8xx and radix:
  - On save, get the value of the associated special register then
    prevent user access.
  - On restore, set back the saved value to the associated special
    register.

On book3s/32:
  - On save, get the value stored in current->thread.kuap and prevent
    user access.
  - On restore, regenerate address range from the stored value and
    reopen read/write access for that range.

Signed-off-by: default avatarChristophe Leroy <christophe.leroy@c-s.fr>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/54f2f74938006b33c55a416674807b42ef222068.1579866752.git.christophe.leroy@c-s.fr
parent 5cd62333
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -153,6 +153,29 @@ static __always_inline void prevent_user_access(void __user *to, const void __us
	kuap_update_sr(mfsrin(addr) | SR_KS, addr, end);	/* set Ks */
}

static inline unsigned long prevent_user_access_return(void)
{
	unsigned long flags = current->thread.kuap;
	unsigned long addr = flags & 0xf0000000;
	unsigned long end = flags << 28;
	void __user *to = (__force void __user *)addr;

	if (flags)
		prevent_user_access(to, to, end - addr, KUAP_READ_WRITE);

	return flags;
}

static inline void restore_user_access(unsigned long flags)
{
	unsigned long addr = flags & 0xf0000000;
	unsigned long end = flags << 28;
	void __user *to = (__force void __user *)addr;

	if (flags)
		allow_user_access(to, to, end - addr, KUAP_READ_WRITE);
}

static inline bool
bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
{
+22 −0
Original line number Diff line number Diff line
@@ -63,6 +63,14 @@
 * because that would require an expensive read/modify write of the AMR.
 */

static inline unsigned long get_kuap(void)
{
	if (!early_mmu_has_feature(MMU_FTR_RADIX_KUAP))
		return 0;

	return mfspr(SPRN_AMR);
}

static inline void set_kuap(unsigned long value)
{
	if (!early_mmu_has_feature(MMU_FTR_RADIX_KUAP))
@@ -98,6 +106,20 @@ static inline void prevent_user_access(void __user *to, const void __user *from,
	set_kuap(AMR_KUAP_BLOCKED);
}

static inline unsigned long prevent_user_access_return(void)
{
	unsigned long flags = get_kuap();

	set_kuap(AMR_KUAP_BLOCKED);

	return flags;
}

static inline void restore_user_access(unsigned long flags)
{
	set_kuap(flags);
}

static inline bool
bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
{
+2 −0
Original line number Diff line number Diff line
@@ -55,6 +55,8 @@ static inline void allow_user_access(void __user *to, const void __user *from,
				     unsigned long size, unsigned long dir) { }
static inline void prevent_user_access(void __user *to, const void __user *from,
				       unsigned long size, unsigned long dir) { }
static inline unsigned long prevent_user_access_return(void) { return 0UL; }
static inline void restore_user_access(unsigned long flags) { }
static inline bool
bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
{
+14 −0
Original line number Diff line number Diff line
@@ -46,6 +46,20 @@ static inline void prevent_user_access(void __user *to, const void __user *from,
	mtspr(SPRN_MD_AP, MD_APG_KUAP);
}

static inline unsigned long prevent_user_access_return(void)
{
	unsigned long flags = mfspr(SPRN_MD_AP);

	mtspr(SPRN_MD_AP, MD_APG_KUAP);

	return flags;
}

static inline void restore_user_access(unsigned long flags)
{
	mtspr(SPRN_MD_AP, flags);
}

static inline bool
bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
{
+2 −3
Original line number Diff line number Diff line
@@ -465,9 +465,8 @@ static __must_check inline bool user_access_begin(const void __user *ptr, size_t
}
#define user_access_begin	user_access_begin
#define user_access_end		prevent_current_access_user

static inline unsigned long user_access_save(void) { return 0UL; }
static inline void user_access_restore(unsigned long flags) { }
#define user_access_save	prevent_user_access_return
#define user_access_restore	restore_user_access

#define unsafe_op_wrap(op, err) do { if (unlikely(op)) goto err; } while (0)
#define unsafe_get_user(x, p, e) unsafe_op_wrap(__get_user_allowed(x, p), e)