From f3c5c95847748917f53bb3c184de66e11387cddb Mon Sep 17 00:00:00 2001 From: kettenis Date: Fri, 4 Nov 2022 16:49:31 +0000 Subject: [PATCH] EFI firmware has bugs which may mean that calling EFI runtime services will fault because it does memory accesses outside of the regions it told us to map. Try to mitigate this by installing a fault handler (using the pcb_onfault mechanism) and bail out using longjmp(9) if we encounter a page fault while executing an EFI runtime services call. Since some firmware bugs result in us executing code that isn't mapped, make kpageflttrap() handle execution faults as well as data faults. ok guenther@ --- sys/arch/amd64/amd64/efi_machdep.c | 26 ++++++++++++++++++++++---- sys/arch/amd64/amd64/locore.S | 7 ++++--- sys/arch/amd64/amd64/trap.c | 5 +++-- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/sys/arch/amd64/amd64/efi_machdep.c b/sys/arch/amd64/amd64/efi_machdep.c index cb005543916..90716cb32e8 100644 --- a/sys/arch/amd64/amd64/efi_machdep.c +++ b/sys/arch/amd64/amd64/efi_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: efi_machdep.c,v 1.2 2022/10/20 18:43:35 kettenis Exp $ */ +/* $OpenBSD: efi_machdep.c,v 1.3 2022/11/04 16:49:31 kettenis Exp $ */ /* * Copyright (c) 2022 Mark Kettenis @@ -60,6 +60,11 @@ void efi_leave(struct efi_softc *); int efi_gettime(struct todr_chip_handle *, struct timeval *); int efi_settime(struct todr_chip_handle *, struct timeval *); +label_t efi_jmpbuf; + +#define efi_enter_check(sc) (setjmp(&efi_jmpbuf) ? \ + (efi_leave(sc), EFAULT) : (efi_enter(sc), 0)) + int efi_match(struct device *parent, void *match, void *aux) { @@ -134,7 +139,8 @@ efi_attach(struct device *parent, struct device *self, void *aux) } efi_leave(sc); - efi_enter(sc); + if (efi_enter_check(sc)) + return; status = sc->sc_rs->GetTime(&time, NULL); efi_leave(sc); if (status != EFI_SUCCESS) @@ -225,6 +231,12 @@ efi_map_runtime(struct efi_softc *sc) } } +void +efi_fault(void) +{ + longjmp(&efi_jmpbuf); +} + void efi_enter(struct efi_softc *sc) { @@ -233,11 +245,15 @@ efi_enter(struct efi_softc *sc) lcr3(sc->sc_pm->pm_pdirpa | (pmap_use_pcid ? PCID_EFI : 0)); fpu_kernel_enter(); + + curpcb->pcb_onfault = (void *)efi_fault; } void efi_leave(struct efi_softc *sc) { + curpcb->pcb_onfault = NULL; + fpu_kernel_exit(); lcr3(sc->sc_cr3); @@ -252,7 +268,8 @@ efi_gettime(struct todr_chip_handle *handle, struct timeval *tv) EFI_TIME time; EFI_STATUS status; - efi_enter(sc); + if (efi_enter_check(sc)) + return EFAULT; status = sc->sc_rs->GetTime(&time, NULL); efi_leave(sc); if (status != EFI_SUCCESS) @@ -296,7 +313,8 @@ efi_settime(struct todr_chip_handle *handle, struct timeval *tv) time.TimeZone = 0; time.Daylight = 0; - efi_enter(sc); + if (efi_enter_check(sc)) + return EFAULT; status = sc->sc_rs->SetTime(&time); efi_leave(sc); if (status != EFI_SUCCESS) diff --git a/sys/arch/amd64/amd64/locore.S b/sys/arch/amd64/amd64/locore.S index 2944720cc40..9be53956f53 100644 --- a/sys/arch/amd64/amd64/locore.S +++ b/sys/arch/amd64/amd64/locore.S @@ -1,4 +1,4 @@ -/* $OpenBSD: locore.S,v 1.128 2022/08/07 23:56:06 guenther Exp $ */ +/* $OpenBSD: locore.S,v 1.129 2022/11/04 16:49:31 kettenis Exp $ */ /* $NetBSD: locore.S,v 1.13 2004/03/25 18:33:17 drochner Exp $ */ /* @@ -105,6 +105,7 @@ */ #include "assym.h" +#include "efi.h" #include "lapic.h" #include "ksyms.h" #include "xen.h" @@ -271,7 +272,7 @@ NENTRY(lgdt) lretq END(lgdt) -#ifdef DDB +#if defined(DDB) || NEFI > 0 ENTRY(setjmp) RETGUARD_SETUP(setjmp, r11) /* @@ -313,7 +314,7 @@ ENTRY(longjmp) ret lfence END(longjmp) -#endif /* DDB */ +#endif /* DDB || NEFI > 0 */ /*****************************************************************************/ diff --git a/sys/arch/amd64/amd64/trap.c b/sys/arch/amd64/amd64/trap.c index 53707fbc99c..ffda7e2609b 100644 --- a/sys/arch/amd64/amd64/trap.c +++ b/sys/arch/amd64/amd64/trap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: trap.c,v 1.91 2022/11/02 07:20:07 guenther Exp $ */ +/* $OpenBSD: trap.c,v 1.92 2022/11/04 16:49:31 kettenis Exp $ */ /* $NetBSD: trap.c,v 1.2 2003/05/04 23:51:56 fvdl Exp $ */ /*- @@ -228,7 +228,8 @@ kpageflttrap(struct trapframe *frame, uint64_t cr2) pcb = &p->p_addr->u_pcb; /* This will only trigger if SMEP is enabled */ - if (cr2 <= VM_MAXUSER_ADDRESS && frame->tf_err & PGEX_I) { + if (pcb->pcb_onfault == NULL && cr2 <= VM_MAXUSER_ADDRESS && + frame->tf_err & PGEX_I) { KERNEL_LOCK(); fault("attempt to execute user address %p " "in supervisor mode", (void *)cr2); -- 2.20.1