x86/alternatives: Disable interrupts and sync when optimizing NOPs in place

apply_alternatives() treats alternatives with the ALT_FLAG_NOT flag set
special as it optimizes the existing NOPs in place.

Unfortunately, this happens with interrupts enabled and does not provide any
form of core synchronization.

So an interrupt hitting in the middle of the update and using the affected code
path will observe a half updated NOP and crash and burn. The following
3 NOP sequence was observed to expose this crash halfway reliably under QEMU
32bit:

0x90 0x90 0x90

which is replaced by the optimized 3 byte NOP:

0x8d 0x76 0x00

So an interrupt can observe:

1) 0x90 0x90 0x90 nop nop nop
2) 0x8d 0x90 0x90 undefined
3) 0x8d 0x76 0x90 lea -0x70(%esi),%esi
4) 0x8d 0x76 0x00 lea 0x0(%esi),%esi

Where only #1 and #4 are true NOPs. The same problem exists for 64bit obviously.

Disable interrupts around this NOP optimization and invoke sync_core()
before re-enabling them.

Fixes: 270a69c4485d ("x86/alternative: Support relocations in alternatives")
Reported-by: Paul Gortmaker <paul.gortmaker@windriver.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/ZT6narvE%2BLxX%2B7Be@windriver.com

authored by Thomas Gleixner and committed by Borislav Petkov (AMD) 2dc41961 3ea1704a

Changed files
+11 -1
arch
x86
kernel
+11 -1
arch/x86/kernel/alternative.c
··· 255 255 } 256 256 } 257 257 258 + static void __init_or_module noinline optimize_nops_inplace(u8 *instr, size_t len) 259 + { 260 + unsigned long flags; 261 + 262 + local_irq_save(flags); 263 + optimize_nops(instr, len); 264 + sync_core(); 265 + local_irq_restore(flags); 266 + } 267 + 258 268 /* 259 269 * In this context, "source" is where the instructions are placed in the 260 270 * section .altinstr_replacement, for example during kernel build by the ··· 448 438 * patch if feature is *NOT* present. 449 439 */ 450 440 if (!boot_cpu_has(a->cpuid) == !(a->flags & ALT_FLAG_NOT)) { 451 - optimize_nops(instr, a->instrlen); 441 + optimize_nops_inplace(instr, a->instrlen); 452 442 continue; 453 443 } 454 444