From 78004e9a87f240df03e2f73120d291763c32e0a7 Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Fri, 21 Aug 2026 01:45:27 +0000 Subject: openrisc: fix arbitrary kernel memory access via or1k_atomic syscall sys_or1k_atomic() (syscall 244 in the "or1k" ABI) takes two user pointers, v1 and v2, and swaps the words they point to in hand-written assembly. l.lwz r29,0(r4) l.lwz r27,0(r5) l.sw 0(r4),r27 l.sw 0(r5),r29 The pointers are not checked with access_ok(). The four memory accesses also have no exception table entries. A caller passes a kernel address as either pointer, and the syscall reads from and writes to it directly. This gives an unprivileged process a kernel read/write primitive. It overwrites kernel data such as the sys_call_table, gaining code execution in kernel context. Check both pointers before entering the critical section. Add fixups for the four memory accesses so faults on valid but unmapped user addresses return -EFAULT. [shorne@gmail.com: fix comment style] Fixes: 9d02a4283e9c ("OpenRISC: Boot code") Cc: stable@vger.kernel.org Signed-off-by: Ali Ahmet Memis Signed-off-by: Stafford Horne --- drivers/misc/lkdtm/powerpc.c | 178 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 drivers/misc/lkdtm/powerpc.c (limited to 'drivers/misc/lkdtm/powerpc.c') diff --git a/drivers/misc/lkdtm/powerpc.c b/drivers/misc/lkdtm/powerpc.c new file mode 100644 index 000000000..6eaac79ea --- /dev/null +++ b/drivers/misc/lkdtm/powerpc.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "lkdtm.h" +#include +#include +#include + +#ifdef CONFIG_PPC_64S_HASH_MMU +/* Inserts new slb entries */ +static void insert_slb_entry(unsigned long p, int ssize, int page_size) +{ + unsigned long flags; + + flags = SLB_VSID_KERNEL | mmu_psize_defs[page_size].sllp; + preempt_disable(); + + asm volatile("slbmte %0,%1" : + : "r" (mk_vsid_data(p, ssize, flags)), + "r" (mk_esid_data(p, ssize, SLB_NUM_BOLTED)) + : "memory"); + isync(); + + asm volatile("slbmte %0,%1" : + : "r" (mk_vsid_data(p, ssize, flags)), + "r" (mk_esid_data(p, ssize, SLB_NUM_BOLTED + 1)) + : "memory"); + isync(); + + preempt_enable(); +} + +/* Inject slb multihit on vmalloc-ed address i.e 0xD00... */ +static int inject_vmalloc_slb_multihit(void) +{ + char *p; + + p = vmalloc(PAGE_SIZE); + if (!p) + return -ENOMEM; + + insert_slb_entry((unsigned long)p, MMU_SEGSIZE_1T, mmu_vmalloc_psize); + /* + * This triggers exception, If handled correctly we must recover + * from this error. + */ + p[0] = '!'; + vfree(p); + return 0; +} + +/* Inject slb multihit on kmalloc-ed address i.e 0xC00... */ +static int inject_kmalloc_slb_multihit(void) +{ + char *p; + + p = kmalloc(2048, GFP_KERNEL); + if (!p) + return -ENOMEM; + + insert_slb_entry((unsigned long)p, MMU_SEGSIZE_1T, mmu_linear_psize); + /* + * This triggers exception, If handled correctly we must recover + * from this error. + */ + p[0] = '!'; + kfree(p); + return 0; +} + +/* + * Few initial SLB entries are bolted. Add a test to inject + * multihit in bolted entry 0. + */ +static void insert_dup_slb_entry_0(void) +{ + unsigned long test_address = PAGE_OFFSET, *test_ptr; + unsigned long esid, vsid; + unsigned long i = 0; + + test_ptr = (unsigned long *)test_address; + preempt_disable(); + + asm volatile("slbmfee %0,%1" : "=r" (esid) : "r" (i)); + asm volatile("slbmfev %0,%1" : "=r" (vsid) : "r" (i)); + + /* for i !=0 we would need to mask out the old entry number */ + asm volatile("slbmte %0,%1" : + : "r" (vsid), + "r" (esid | SLB_NUM_BOLTED) + : "memory"); + isync(); + + asm volatile("slbmfee %0,%1" : "=r" (esid) : "r" (i)); + asm volatile("slbmfev %0,%1" : "=r" (vsid) : "r" (i)); + + /* for i !=0 we would need to mask out the old entry number */ + asm volatile("slbmte %0,%1" : + : "r" (vsid), + "r" (esid | (SLB_NUM_BOLTED + 1)) + : "memory"); + isync(); + + pr_info("%s accessing test address 0x%lx: 0x%lx\n", + __func__, test_address, *test_ptr); + + preempt_enable(); +} +#endif /* CONFIG_PPC_64S_HASH_MMU */ + +static __always_inline void tlbiel_va(unsigned long va, + unsigned long pid, + unsigned long ap, + unsigned long ric) +{ + unsigned long rb, rs, prs, r; + + rb = va & ~(PPC_BITMASK(52, 63)); + rb |= ap << PPC_BITLSHIFT(58); + rs = pid << PPC_BITLSHIFT(31); + + prs = 1; /* process scoped */ + r = 1; /* radix format */ + + /* + * Trigger an MCE by issuing radix tlbiel with an invalid operand combination. + * The combination of RIC = 2 with IS = 0 (Invalidation selector specified + * in the RB register) is invalid. + * This invalid combination causes hardware to raise a machine check. + */ + asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1) + : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); +} + +static void lkdtm_PPC_SLB_MULTIHIT(void) +{ +#ifdef CONFIG_PPC_64S_HASH_MMU + if (!radix_enabled()) { + pr_info("Injecting SLB multihit errors\n"); + /* + * These need not be separate tests, And they do pretty + * much same thing. In any case we must recover from the + * errors introduced by these functions, machine would not + * survive these tests in case of failure to handle. + */ + inject_vmalloc_slb_multihit(); + inject_kmalloc_slb_multihit(); + insert_dup_slb_entry_0(); + pr_info("Recovered from SLB multihit errors\n"); + } else { + pr_err("XFAIL: This test is for ppc64 and with hash mode MMU only\n"); + } +#else + pr_err("XFAIL: This test requires CONFIG_PPC_64S_HASH_MMU\n"); +#endif +} + +static void lkdtm_PPC_RADIX_TLBIEL(void) +{ + unsigned long addr = PAGE_OFFSET; + + if (radix_enabled()) { + pr_info("Injecting Radix TLB invalidation MCE\n"); + tlbiel_va(addr, 0, 0, RIC_FLUSH_ALL); + pr_info("Recovered from radix tlbiel attempt\n"); + } else { + pr_err("XFAIL: This test is for ppc64 and with radix mode MMU only\n"); + } +} + +static struct crashtype crashtypes[] = { + CRASHTYPE(PPC_SLB_MULTIHIT), + CRASHTYPE(PPC_RADIX_TLBIEL), +}; + +struct crashtype_category powerpc_crashtypes = { + .crashtypes = crashtypes, + .len = ARRAY_SIZE(crashtypes), +}; -- cgit v1.3.1