When one page is already hwpoisoned by MCE AO action, processes may not be killed, processes mapping this page may make a syscall include this page and result to trigger a VM_FAULT_HWPOISON fault, as it's in kernel mode it may be fixed by fixup_exception, current code will just return error code to user code. This is not sufficient, we should send a SIGBUS to the process and log the info to console, as we can't trust the process will handle the error correctly. Suggested-by: Feng Yang <yangfeng1@xxxxxxxxxxxx> Signed-off-by: Aili Yao <yaoaili@xxxxxxxxxxxx> --- arch/x86/mm/fault.c | 62 +++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 08f5f74cf989..62df798abb56 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -617,6 +617,30 @@ static void set_signal_archinfo(unsigned long address, tsk->thread.cr2 = address; } +static int +do_sigbus_mceerr(unsigned long error_code, unsigned long address, vm_fault_t fault, int prepared) +{ + struct task_struct *tsk = current; + unsigned int lsb = 0; + + if (fault & (VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE)) { + pr_err( + "MCE: Killing %s:%d due to hardware memory corruption fault at %lx\n", + tsk->comm, tsk->pid, address); + if (fault & VM_FAULT_HWPOISON_LARGE) + lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault)); + if (fault & VM_FAULT_HWPOISON) + lsb = PAGE_SHIFT; + if (!prepared) { + sanitize_error_code(address, &error_code); + set_signal_archinfo(address, error_code); + } + force_sig_mceerr(BUS_MCEERR_AR, (void __user *)address, lsb); + return 1; + } + return 0; +} + static noinline void page_fault_oops(struct pt_regs *regs, unsigned long error_code, unsigned long address) @@ -694,7 +718,7 @@ page_fault_oops(struct pt_regs *regs, unsigned long error_code, static noinline void kernelmode_fixup_or_oops(struct pt_regs *regs, unsigned long error_code, - unsigned long address, int signal, int si_code) + unsigned long address, int signal, int si_code, vm_fault_t fault) { WARN_ON_ONCE(user_mode(regs)); @@ -714,12 +738,17 @@ kernelmode_fixup_or_oops(struct pt_regs *regs, unsigned long error_code, * In this case we need to make sure we're not recursively * faulting through the emulate_vsyscall() logic. */ + + /* Sending MCERR Sigbus for page fault error from hwpoison */ + if (IS_ENABLED(CONFIG_MEMORY_FAILURE) + && do_sigbus_mceerr(error_code, address, fault, 0)) + return; + if (current->thread.sig_on_uaccess_err && signal) { sanitize_error_code(address, &error_code); set_signal_archinfo(address, error_code); - /* XXX: hwpoison faults will set the wrong code. */ force_sig_fault(signal, si_code, (void __user *)address); } @@ -782,7 +811,7 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code, struct task_struct *tsk = current; if (!user_mode(regs)) { - kernelmode_fixup_or_oops(regs, error_code, address, pkey, si_code); + kernelmode_fixup_or_oops(regs, error_code, address, pkey, si_code, 0); return; } @@ -914,7 +943,7 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address, { /* Kernel mode? Handle exceptions or die: */ if (!user_mode(regs)) { - kernelmode_fixup_or_oops(regs, error_code, address, SIGBUS, BUS_ADRERR); + kernelmode_fixup_or_oops(regs, error_code, address, SIGBUS, BUS_ADRERR, fault); return; } @@ -929,22 +958,11 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address, set_signal_archinfo(address, error_code); -#ifdef CONFIG_MEMORY_FAILURE - if (fault & (VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE)) { - struct task_struct *tsk = current; - unsigned lsb = 0; - - pr_err( - "MCE: Killing %s:%d due to hardware memory corruption fault at %lx\n", - tsk->comm, tsk->pid, address); - if (fault & VM_FAULT_HWPOISON_LARGE) - lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault)); - if (fault & VM_FAULT_HWPOISON) - lsb = PAGE_SHIFT; - force_sig_mceerr(BUS_MCEERR_AR, (void __user *)address, lsb); + /* Sending MCERR Sigbus for page fault error from hwpoison */ + if (IS_ENABLED(CONFIG_MEMORY_FAILURE) + && do_sigbus_mceerr(error_code, address, fault, 1)) return; - } -#endif + force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)address); } @@ -1380,7 +1398,7 @@ void do_user_addr_fault(struct pt_regs *regs, */ if (!user_mode(regs)) kernelmode_fixup_or_oops(regs, error_code, address, - SIGBUS, BUS_ADRERR); + SIGBUS, BUS_ADRERR, 0); return; } @@ -1400,7 +1418,7 @@ void do_user_addr_fault(struct pt_regs *regs, return; if (fatal_signal_pending(current) && !user_mode(regs)) { - kernelmode_fixup_or_oops(regs, error_code, address, 0, 0); + kernelmode_fixup_or_oops(regs, error_code, address, 0, 0, 0); return; } @@ -1408,7 +1426,7 @@ void do_user_addr_fault(struct pt_regs *regs, /* Kernel mode? Handle exceptions or die: */ if (!user_mode(regs)) { kernelmode_fixup_or_oops(regs, error_code, address, - SIGSEGV, SEGV_MAPERR); + SIGSEGV, SEGV_MAPERR, 0); return; } base-commit: 167104452673f6c1b0e7bdbdf6a4b25f862e2319 prerequisite-patch-id: 6d1e0f23558f83f851ae883633b5c498f0c826c6 prerequisite-patch-id: 2dd1d48b132e6fa0c7b564dbbd9af2e667ebc997 prerequisite-patch-id: 4fa3543c8706f54bfc0b12b787222c65ff701c10 prerequisite-patch-id: 7465d0fe35a243e23f21a60b3595c09f4a95bdc2 prerequisite-patch-id: 0e7f44bbfa3911af10aa208dee5a8f7fb33f54b7 prerequisite-patch-id: 19bdc10dd055ae8761a8a87f6912f84b4bda68be prerequisite-patch-id: df466945a2b3150abca123da0467b52220c3cc19 prerequisite-patch-id: 6678a8efeced8907cbdf167221b7d8c0deaa6c44 prerequisite-patch-id: d2270eca0467bbd5e8575fda9f18ddf7943f5562 prerequisite-patch-id: 97ab827e60b94faa6e4fc9f955aa88c4f51ceb5a -- 2.25.1