| From f6629c58b006a9f0be2d793ef5265675fbbc9a4e Mon Sep 17 00:00:00 2001 |
| From: Christophe Leroy <christophe.leroy@c-s.fr> |
| Date: Fri, 24 Jan 2020 11:54:40 +0000 |
| Subject: [PATCH] powerpc/32s: Fix bad_kuap_fault() |
| |
| commit 6ec20aa2e510b6297906c45f009aa08b2d97269a upstream. |
| |
| At the moment, bad_kuap_fault() reports a fault only if a bad access |
| to userspace occurred while access to userspace was not granted. |
| |
| But if a fault occurs for a write outside the allowed userspace |
| segment(s) that have been unlocked, bad_kuap_fault() fails to |
| detect it and the kernel loops forever in do_page_fault(). |
| |
| Fix it by checking that the accessed address is within the allowed |
| range. |
| |
| Fixes: a68c31fc01ef ("powerpc/32s: Implement Kernel Userspace Access Protection") |
| Cc: stable@vger.kernel.org # v5.2+ |
| 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/f48244e9485ada0a304ed33ccbb8da271180c80d.1579866752.git.christophe.leroy@c-s.fr |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/arch/powerpc/include/asm/book3s/32/kup.h b/arch/powerpc/include/asm/book3s/32/kup.h |
| index f9dc597b0b86..d88008c8eb85 100644 |
| --- a/arch/powerpc/include/asm/book3s/32/kup.h |
| +++ b/arch/powerpc/include/asm/book3s/32/kup.h |
| @@ -131,12 +131,17 @@ static inline void prevent_user_access(void __user *to, const void __user *from, |
| kuap_update_sr(mfsrin(addr) | SR_KS, addr, end); /* set Ks */ |
| } |
| |
| -static inline bool bad_kuap_fault(struct pt_regs *regs, bool is_write) |
| +static inline bool |
| +bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write) |
| { |
| + unsigned long begin = regs->kuap & 0xf0000000; |
| + unsigned long end = regs->kuap << 28; |
| + |
| if (!is_write) |
| return false; |
| |
| - return WARN(!regs->kuap, "Bug: write fault blocked by segment registers !"); |
| + return WARN(address < begin || address >= end, |
| + "Bug: write fault blocked by segment registers !"); |
| } |
| |
| #endif /* CONFIG_PPC_KUAP */ |
| diff --git a/arch/powerpc/include/asm/book3s/64/kup-radix.h b/arch/powerpc/include/asm/book3s/64/kup-radix.h |
| index f254de956d6a..dbbd22cb80f5 100644 |
| --- a/arch/powerpc/include/asm/book3s/64/kup-radix.h |
| +++ b/arch/powerpc/include/asm/book3s/64/kup-radix.h |
| @@ -95,7 +95,8 @@ static inline void prevent_user_access(void __user *to, const void __user *from, |
| set_kuap(AMR_KUAP_BLOCKED); |
| } |
| |
| -static inline bool bad_kuap_fault(struct pt_regs *regs, bool is_write) |
| +static inline bool |
| +bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write) |
| { |
| return WARN(mmu_has_feature(MMU_FTR_RADIX_KUAP) && |
| (regs->kuap & (is_write ? AMR_KUAP_BLOCK_WRITE : AMR_KUAP_BLOCK_READ)), |
| diff --git a/arch/powerpc/include/asm/kup.h b/arch/powerpc/include/asm/kup.h |
| index 5b5e39643a27..812e66f31934 100644 |
| --- a/arch/powerpc/include/asm/kup.h |
| +++ b/arch/powerpc/include/asm/kup.h |
| @@ -45,7 +45,11 @@ static inline void allow_user_access(void __user *to, const void __user *from, |
| unsigned long size) { } |
| static inline void prevent_user_access(void __user *to, const void __user *from, |
| unsigned long size) { } |
| -static inline bool bad_kuap_fault(struct pt_regs *regs, bool is_write) { return false; } |
| +static inline bool |
| +bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write) |
| +{ |
| + return false; |
| +} |
| #endif /* CONFIG_PPC_KUAP */ |
| |
| static inline void allow_read_from_user(const void __user *from, unsigned long size) |
| diff --git a/arch/powerpc/include/asm/nohash/32/kup-8xx.h b/arch/powerpc/include/asm/nohash/32/kup-8xx.h |
| index 1c3133b5f86a..9d1fe811aa5d 100644 |
| --- a/arch/powerpc/include/asm/nohash/32/kup-8xx.h |
| +++ b/arch/powerpc/include/asm/nohash/32/kup-8xx.h |
| @@ -45,7 +45,8 @@ static inline void prevent_user_access(void __user *to, const void __user *from, |
| mtspr(SPRN_MD_AP, MD_APG_KUAP); |
| } |
| |
| -static inline bool bad_kuap_fault(struct pt_regs *regs, bool is_write) |
| +static inline bool |
| +bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write) |
| { |
| return WARN(!((regs->kuap ^ MD_APG_KUAP) & 0xf0000000), |
| "Bug: fault blocked by AP register !"); |
| diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c |
| index ec6b7ad70659..6aeb468ef955 100644 |
| --- a/arch/powerpc/mm/fault.c |
| +++ b/arch/powerpc/mm/fault.c |
| @@ -254,7 +254,7 @@ static bool bad_kernel_fault(struct pt_regs *regs, unsigned long error_code, |
| |
| // Read/write fault in a valid region (the exception table search passed |
| // above), but blocked by KUAP is bad, it can never succeed. |
| - if (bad_kuap_fault(regs, is_write)) |
| + if (bad_kuap_fault(regs, address, is_write)) |
| return true; |
| |
| // What's left? Kernel fault on user in well defined regions (extable |
| -- |
| 2.7.4 |
| |