Skip to content

Commit 49b39ec

Browse files
committed
powerpc/64s: Fix entry flush patching w/strict RWX & hash
The entry flush mitigation can be enabled/disabled at runtime. When this happens it results in the kernel patching its own instructions to enable/disable the mitigation sequence. With strict kernel RWX enabled instruction patching happens via a secondary mapping of the kernel text, so that we don't have to make the primary mapping writable. With the hash MMU this leads to a hash fault, which causes us to execute the exception entry which contains the entry flush mitigation. This means we end up executing the entry flush in a semi-patched state, ie. after we have patched the first instruction but before we patch the second or third instruction of the sequence. On machines with updated firmware the entry flush is a series of special nops, and it's safe to to execute in a semi-patched state. However when using the fallback flush the sequence is mflr/branch/mtlr, and so it's not safe to execute if we have patched out the mflr but not the other two instructions. Doing so leads to us corrputing LR, leading to an oops, for example: # echo 0 > /sys/kernel/debug/powerpc/entry_flush kernel tried to execute exec-protected page (c000000002971000) - exploit attempt? (uid: 0) BUG: Unable to handle kernel instruction fetch Faulting instruction address: 0xc000000002971000 Oops: Kernel access of bad area, sig: 11 [#1] LE PAGE_SIZE=64K MMU=Hash SMP NR_CPUS=2048 NUMA pSeries CPU: 0 PID: 2215 Comm: bash Not tainted 5.13.0-rc1-00010-gda3bb206c9ce #1 NIP: c000000002971000 LR: c000000002971000 CTR: c000000000120c40 REGS: c000000013243840 TRAP: 0400 Not tainted (5.13.0-rc1-00010-gda3bb206c9ce) MSR: 8000000010009033 <SF,EE,ME,IR,DR,RI,LE> CR: 4842848 XER: 00000000 ... NIP 0xc000000002971000 LR 0xc000000002971000 Call Trace: do_patch_instruction+0xc4/0x340 (unreliable) do_entry_flush_fixups+0x100/0x3b0 entry_flush_set+0x50/0xe0 simple_attr_write+0x160/0x1a0 full_proxy_write+0x8c/0x110 vfs_write+0xf0/0x340 ksys_write+0x84/0x140 system_call_exception+0x164/0x2d0 system_call_common+0xec/0x278 The simplest fix is to change the order in which we patch the instructions, so that the sequence is always safe to execute. For the non-fallback flushes it doesn't matter what order we patch in. Fixes: bd573a8 ("powerpc/mm/64s: Allow STRICT_KERNEL_RWX again") Signed-off-by: Michael Ellerman <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent aec86b0 commit 49b39ec

File tree

1 file changed

+43
-16
lines changed

1 file changed

+43
-16
lines changed

arch/powerpc/lib/feature-fixups.c

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -325,22 +325,48 @@ static int __do_entry_flush_fixups(void *data)
325325
if (types & L1D_FLUSH_MTTRIG)
326326
instrs[i++] = 0x7c12dba6; /* mtspr TRIG2,r0 (SPR #882) */
327327

328+
/*
329+
* If we're patching in or out the fallback flush we need to be careful about the
330+
* order in which we patch instructions. That's because it's possible we could
331+
* take a page fault after patching one instruction, so the sequence of
332+
* instructions must be safe even in a half patched state.
333+
*
334+
* To make that work, when patching in the fallback flush we patch in this order:
335+
* - the mflr (dest)
336+
* - the mtlr (dest + 2)
337+
* - the branch (dest + 1)
338+
*
339+
* That ensures the sequence is safe to execute at any point. In contrast if we
340+
* patch the mtlr last, it's possible we could return from the branch and not
341+
* restore LR, leading to a crash later.
342+
*
343+
* When patching out the fallback flush (either with nops or another flush type),
344+
* we patch in this order:
345+
* - the branch (dest + 1)
346+
* - the mtlr (dest + 2)
347+
* - the mflr (dest)
348+
*
349+
* Note we are protected by stop_machine() from other CPUs executing the code in a
350+
* semi-patched state.
351+
*/
352+
328353
start = PTRRELOC(&__start___entry_flush_fixup);
329354
end = PTRRELOC(&__stop___entry_flush_fixup);
330355
for (i = 0; start < end; start++, i++) {
331356
dest = (void *)start + *start;
332357

333358
pr_devel("patching dest %lx\n", (unsigned long)dest);
334359

335-
patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0]));
336-
337-
if (types == L1D_FLUSH_FALLBACK)
338-
patch_branch((struct ppc_inst *)(dest + 1), (unsigned long)&entry_flush_fallback,
339-
BRANCH_SET_LINK);
340-
else
360+
if (types == L1D_FLUSH_FALLBACK) {
361+
patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0]));
362+
patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2]));
363+
patch_branch((struct ppc_inst *)(dest + 1),
364+
(unsigned long)&entry_flush_fallback, BRANCH_SET_LINK);
365+
} else {
341366
patch_instruction((struct ppc_inst *)(dest + 1), ppc_inst(instrs[1]));
342-
343-
patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2]));
367+
patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2]));
368+
patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0]));
369+
}
344370
}
345371

346372
start = PTRRELOC(&__start___scv_entry_flush_fixup);
@@ -350,15 +376,16 @@ static int __do_entry_flush_fixups(void *data)
350376

351377
pr_devel("patching dest %lx\n", (unsigned long)dest);
352378

353-
patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0]));
354-
355-
if (types == L1D_FLUSH_FALLBACK)
356-
patch_branch((struct ppc_inst *)(dest + 1), (unsigned long)&scv_entry_flush_fallback,
357-
BRANCH_SET_LINK);
358-
else
379+
if (types == L1D_FLUSH_FALLBACK) {
380+
patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0]));
381+
patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2]));
382+
patch_branch((struct ppc_inst *)(dest + 1),
383+
(unsigned long)&scv_entry_flush_fallback, BRANCH_SET_LINK);
384+
} else {
359385
patch_instruction((struct ppc_inst *)(dest + 1), ppc_inst(instrs[1]));
360-
361-
patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2]));
386+
patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2]));
387+
patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0]));
388+
}
362389
}
363390

364391

0 commit comments

Comments
 (0)