| From 1da4a0272c5469169f78cd76cf175ff984f52f06 Mon Sep 17 00:00:00 2001 |
| From: Michael Neuling <mikey@neuling.org> |
| Date: Tue, 13 Oct 2020 15:37:40 +1100 |
| Subject: powerpc: Fix undetected data corruption with P9N DD2.1 VSX CI load emulation |
| |
| From: Michael Neuling <mikey@neuling.org> |
| |
| commit 1da4a0272c5469169f78cd76cf175ff984f52f06 upstream. |
| |
| __get_user_atomic_128_aligned() stores to kaddr using stvx which is a |
| VMX store instruction, hence kaddr must be 16 byte aligned otherwise |
| the store won't occur as expected. |
| |
| Unfortunately when we call __get_user_atomic_128_aligned() in |
| p9_hmi_special_emu(), the buffer we pass as kaddr (ie. vbuf) isn't |
| guaranteed to be 16B aligned. This means that the write to vbuf in |
| __get_user_atomic_128_aligned() has the bottom bits of the address |
| truncated. This results in other local variables being |
| overwritten. Also vbuf will not contain the correct data which results |
| in the userspace emulation being wrong and hence undetected user data |
| corruption. |
| |
| In the past we've been mostly lucky as vbuf has ended up aligned but |
| this is fragile and isn't always true. CONFIG_STACKPROTECTOR in |
| particular can change the stack arrangement enough that our luck runs |
| out. |
| |
| This issue only occurs on POWER9 Nimbus <= DD2.1 bare metal. |
| |
| The fix is to align vbuf to a 16 byte boundary. |
| |
| Fixes: 5080332c2c89 ("powerpc/64s: Add workaround for P9 vector CI load issue") |
| Cc: stable@vger.kernel.org # v4.15+ |
| Signed-off-by: Michael Neuling <mikey@neuling.org> |
| Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> |
| Link: https://lore.kernel.org/r/20201013043741.743413-1-mikey@neuling.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/powerpc/kernel/traps.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| --- a/arch/powerpc/kernel/traps.c |
| +++ b/arch/powerpc/kernel/traps.c |
| @@ -889,7 +889,7 @@ static void p9_hmi_special_emu(struct pt |
| { |
| unsigned int ra, rb, t, i, sel, instr, rc; |
| const void __user *addr; |
| - u8 vbuf[16], *vdst; |
| + u8 vbuf[16] __aligned(16), *vdst; |
| unsigned long ea, msr, msr_mask; |
| bool swap; |
| |