x86/{fault,efi}: Fix and rename efi_recover_from_page_fault()

efi_recover_from_page_fault() doesn't recover -- it does a special EFI
mini-oops.  Rename it to make it clear that it crashes.

While renaming it, I noticed a blatant bug: a page fault oops in a
different thread happening concurrently with an EFI runtime service call
would be misinterpreted as an EFI page fault.  Fix that.

This isn't quite exact. The situation could be improved by using a
special CS for calls into EFI.

 [ bp: Massage commit message and simplify in interrupt check. ]

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/f43b1e80830dc78ed60ed8b0826f4f189254570c.1612924255.git.luto@kernel.org
This commit is contained in:
Andy Lutomirski 2021-02-09 18:33:46 -08:00 committed by Borislav Petkov
parent ca24728378
commit c46f52231e
3 changed files with 20 additions and 11 deletions

View File

@ -150,7 +150,7 @@ extern void __init efi_apply_memmap_quirks(void);
extern int __init efi_reuse_config(u64 tables, int nr_tables);
extern void efi_delete_dummy_variable(void);
extern void efi_switch_mm(struct mm_struct *mm);
extern void efi_recover_from_page_fault(unsigned long phys_addr);
extern void efi_crash_gracefully_on_page_fault(unsigned long phys_addr);
extern void efi_free_boot_services(void);
/* kexec external ABI */

View File

@ -16,7 +16,7 @@
#include <linux/prefetch.h> /* prefetchw */
#include <linux/context_tracking.h> /* exception_enter(), ... */
#include <linux/uaccess.h> /* faulthandler_disabled() */
#include <linux/efi.h> /* efi_recover_from_page_fault()*/
#include <linux/efi.h> /* efi_crash_gracefully_on_page_fault()*/
#include <linux/mm_types.h>
#include <asm/cpufeature.h> /* boot_cpu_has, ... */
@ -25,7 +25,7 @@
#include <asm/vsyscall.h> /* emulate_vsyscall */
#include <asm/vm86.h> /* struct vm86 */
#include <asm/mmu_context.h> /* vma_pkey() */
#include <asm/efi.h> /* efi_recover_from_page_fault()*/
#include <asm/efi.h> /* efi_crash_gracefully_on_page_fault()*/
#include <asm/desc.h> /* store_idt(), ... */
#include <asm/cpu_entry_area.h> /* exception stack */
#include <asm/pgtable_areas.h> /* VMALLOC_START, ... */
@ -701,11 +701,12 @@ page_fault_oops(struct pt_regs *regs, unsigned long error_code,
#endif
/*
* Buggy firmware could access regions which might page fault, try to
* recover from such faults.
* Buggy firmware could access regions which might page fault. If
* this happens, EFI has a special OOPS path that will try to
* avoid hanging the system.
*/
if (IS_ENABLED(CONFIG_EFI))
efi_recover_from_page_fault(address);
efi_crash_gracefully_on_page_fault(address);
oops:
/*

View File

@ -687,15 +687,25 @@ int efi_capsule_setup_info(struct capsule_info *cap_info, void *kbuff,
* @return: Returns, if the page fault is not handled. This function
* will never return if the page fault is handled successfully.
*/
void efi_recover_from_page_fault(unsigned long phys_addr)
void efi_crash_gracefully_on_page_fault(unsigned long phys_addr)
{
if (!IS_ENABLED(CONFIG_X86_64))
return;
/*
* Make sure that an efi runtime service caused the page fault.
* If we get an interrupt/NMI while processing an EFI runtime service
* then this is a regular OOPS, not an EFI failure.
*/
if (efi_rts_work.efi_rts_id == EFI_NONE)
if (in_interrupt())
return;
/*
* Make sure that an efi runtime service caused the page fault.
* READ_ONCE() because we might be OOPSing in a different thread,
* and we don't want to trip KTSAN while trying to OOPS.
*/
if (READ_ONCE(efi_rts_work.efi_rts_id) == EFI_NONE ||
current_work() != &efi_rts_work.work)
return;
/*
@ -747,6 +757,4 @@ void efi_recover_from_page_fault(unsigned long phys_addr)
set_current_state(TASK_IDLE);
schedule();
}
return;
}