diff options
author | Christophe Leroy <christophe.leroy@c-s.fr> | 2020-01-24 11:54:45 +0000 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2020-01-28 23:14:44 +1100 |
commit | 3d7dfd632f9b60cfce069b4da517e6b1a1c3f613 (patch) | |
tree | 02ec1e93d286d7c49dce90a4294f8ddc1edb3af4 /arch/powerpc | |
parent | 5cd623333e7cf4e3a334c70529268b65f2a6c2c7 (diff) | |
download | linux-stable-3d7dfd632f9b60cfce069b4da517e6b1a1c3f613.tar.gz linux-stable-3d7dfd632f9b60cfce069b4da517e6b1a1c3f613.tar.bz2 linux-stable-3d7dfd632f9b60cfce069b4da517e6b1a1c3f613.zip |
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: Christophe Leroy <christophe.leroy@c-s.fr>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/54f2f74938006b33c55a416674807b42ef222068.1579866752.git.christophe.leroy@c-s.fr
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/include/asm/book3s/32/kup.h | 23 | ||||
-rw-r--r-- | arch/powerpc/include/asm/book3s/64/kup-radix.h | 22 | ||||
-rw-r--r-- | arch/powerpc/include/asm/kup.h | 2 | ||||
-rw-r--r-- | arch/powerpc/include/asm/nohash/32/kup-8xx.h | 14 | ||||
-rw-r--r-- | arch/powerpc/include/asm/uaccess.h | 5 |
5 files changed, 63 insertions, 3 deletions
diff --git a/arch/powerpc/include/asm/book3s/32/kup.h b/arch/powerpc/include/asm/book3s/32/kup.h index 17e069291c72..3c0ba22dc360 100644 --- a/arch/powerpc/include/asm/book3s/32/kup.h +++ b/arch/powerpc/include/asm/book3s/32/kup.h @@ -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) { diff --git a/arch/powerpc/include/asm/book3s/64/kup-radix.h b/arch/powerpc/include/asm/book3s/64/kup-radix.h index a0263e94df33..90dd3a3fc8c7 100644 --- a/arch/powerpc/include/asm/book3s/64/kup-radix.h +++ b/arch/powerpc/include/asm/book3s/64/kup-radix.h @@ -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) { diff --git a/arch/powerpc/include/asm/kup.h b/arch/powerpc/include/asm/kup.h index c3ce7e8ae9ea..92bcd1a26d73 100644 --- a/arch/powerpc/include/asm/kup.h +++ b/arch/powerpc/include/asm/kup.h @@ -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) { diff --git a/arch/powerpc/include/asm/nohash/32/kup-8xx.h b/arch/powerpc/include/asm/nohash/32/kup-8xx.h index 1d70c80366fd..85ed2390fb99 100644 --- a/arch/powerpc/include/asm/nohash/32/kup-8xx.h +++ b/arch/powerpc/include/asm/nohash/32/kup-8xx.h @@ -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) { diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h index af905d7fc1df..2f500debae21 100644 --- a/arch/powerpc/include/asm/uaccess.h +++ b/arch/powerpc/include/asm/uaccess.h @@ -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) |