From f9e5938ace2cc8ecf15815649481266017135757 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sun, 27 Nov 2022 09:45:26 +0100 Subject: [PATCH] s390/nmi: rework register validation handling If a machine check happens in kernel mode, and the machine check interruption code indicates that e.g. vector register contents in the machine check area are not valid, the logic is to kill current. The idea behind this was that if within kernel context vector registers are not used then it is sufficient to kill the current user space process to avoid that it continues with potentially corrupt register contents. This however does not necessarily work, since the current code does not take into account that a machine check can also happen when a kernel thread is running (= no user space context), and in addition there is no way to distinguish between the "previous" and "next" user process task, if the machine check happens when a task switch happens. Given that machine checks with invalid saved register contents in the machine check save area are extremely rare, simplify the logic: if register contents are invalid and the previous context was kernel mode, stop the whole machine. If the previous context was user mode, kill the corresponding task. Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/kernel/nmi.c | 64 ++++++++------------------------------------------ 1 file changed, 10 insertions(+), 54 deletions(-) diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index f384f2d..3518ba6 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c @@ -180,10 +180,10 @@ void noinstr s390_handle_mcck(struct pt_regs *regs) trace_hardirqs_on(); } /* - * returns 0 if all required registers are available + * returns 0 if register contents could be validated * returns 1 otherwise */ -static int notrace s390_validate_registers(union mci mci, int umode) +static int notrace s390_validate_registers(union mci mci) { struct mcesa *mcesa; void *fpt_save_area; @@ -194,45 +194,15 @@ static int notrace s390_validate_registers(union mci mci, int umode) kill_task = 0; zero = 0; - if (!mci.gr) { - /* - * General purpose registers couldn't be restored and have - * unknown contents. Stop system or terminate process. - */ - if (!umode) - s390_handle_damage(); + if (!mci.gr || !mci.fp) kill_task = 1; - } - if (!mci.fp) { - /* - * Floating point registers can't be restored. If the - * kernel currently uses floating point registers the - * system is stopped. If the process has its floating - * pointer registers loaded it is terminated. - */ - if (S390_lowcore.fpu_flags & KERNEL_VXR_V0V7) - s390_handle_damage(); - if (!test_cpu_flag(CIF_FPU)) - kill_task = 1; - } fpt_save_area = &S390_lowcore.floating_pt_save_area; if (!mci.fc) { - /* - * Floating point control register can't be restored. - * If the kernel currently uses the floating pointer - * registers and needs the FPC register the system is - * stopped. If the process has its floating pointer - * registers loaded it is terminated. Otherwise the - * FPC is just validated. - */ - if (S390_lowcore.fpu_flags & KERNEL_FPC) - s390_handle_damage(); + kill_task = 1; asm volatile( " lfpc %0\n" : : "Q" (zero)); - if (!test_cpu_flag(CIF_FPU)) - kill_task = 1; } else { asm volatile( " lfpc %0\n" @@ -274,19 +244,8 @@ static int notrace s390_validate_registers(union mci mci, int umode) * appropriate actions. The host vector or FPU values have been * saved by KVM and will be restored by KVM. */ - if (!mci.vr && !test_cpu_flag(CIF_MCCK_GUEST)) { - /* - * Vector registers can't be restored. If the kernel - * currently uses vector registers the system is - * stopped. If the process has its vector registers - * loaded it is terminated. Otherwise just validate - * the registers. - */ - if (S390_lowcore.fpu_flags & KERNEL_VXR) - s390_handle_damage(); - if (!test_cpu_flag(CIF_FPU)) - kill_task = 1; - } + if (!mci.vr && !test_cpu_flag(CIF_MCCK_GUEST)) + kill_task = 1; cr0.val = S390_lowcore.cregs_save_area[0]; cr0.afp = cr0.vx = 1; __ctl_load(cr0.val, 0, 0); @@ -305,13 +264,8 @@ static int notrace s390_validate_registers(union mci mci, int umode) : : "a" (&S390_lowcore.access_regs_save_area) : "memory"); - if (!mci.ar) { - /* - * Access registers have unknown contents. - * Terminating task. - */ + if (!mci.ar) kill_task = 1; - } /* Validate guarded storage registers */ cr2.val = S390_lowcore.cregs_save_area[2]; if (cr2.gse) { @@ -450,7 +404,9 @@ int notrace s390_do_machine_check(struct pt_regs *regs) s390_handle_damage(); } } - if (s390_validate_registers(mci, user_mode(regs))) { + if (s390_validate_registers(mci)) { + if (!user_mode(regs)) + s390_handle_damage(); /* * Couldn't restore all register contents for the * user space process -> mark task for termination. -- 2.7.4