Re: git: f29942229d24 - main - Read the arm64 far early in el0 exceptions
- Reply: Jessica Clarke : "Re: git: f29942229d24 - main - Read the arm64 far early in el0 exceptions"
- Reply: Andrew Turner : "Re: git: f29942229d24 - main - Read the arm64 far early in el0 exceptions"
- In reply to: Andrew Turner : "git: f29942229d24 - main - Read the arm64 far early in el0 exceptions"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 02 Feb 2023 21:00:57 UTC
On 2 Feb 2023, at 16:48, Andrew Turner <andrew@FreeBSD.org> wrote: > > The branch main has been updated by andrew: > > URL: https://cgit.FreeBSD.org/src/commit/?id=f29942229d24ebb8b98f8c5d02f3c8632648007e > > commit f29942229d24ebb8b98f8c5d02f3c8632648007e > Author: Andrew Turner <andrew@FreeBSD.org> > AuthorDate: 2023-01-25 17:47:39 +0000 > Commit: Andrew Turner <andrew@FreeBSD.org> > CommitDate: 2023-02-02 16:43:15 +0000 > > Read the arm64 far early in el0 exceptions > > When handling userspace exceptions on arm64 we need to dereference the > current thread pointer. If this is being promoted/demoted there is a > small window where it will cause another exception to be hit. As this > second exception will set the fault address register we will read the > incorrect value in the userspace exception handler. > > Fix this be always reading the fault address before dereferencing the > current thread pointer. > > Reported by: olivier@ > Reviewed by: markj > Sponsored by: Arm Ltd > Differential Revision: https://reviews.freebsd.org/D38196 > --- > sys/arm64/arm64/exception.S | 15 +++++++++++++++ > sys/arm64/arm64/trap.c | 26 +++++++------------------- > 2 files changed, 22 insertions(+), 19 deletions(-) > > diff --git a/sys/arm64/arm64/exception.S b/sys/arm64/arm64/exception.S > index 4a74358afeb9..55bac5e5228a 100644 > --- a/sys/arm64/arm64/exception.S > +++ b/sys/arm64/arm64/exception.S > @@ -212,10 +212,25 @@ ENTRY(handle_el1h_irq) > END(handle_el1h_irq) > > ENTRY(handle_el0_sync) > + /* > + * Read the fault address early. The current thread structure may > + * be transiently unmapped if it is part of a memory range being > + * promoted or demoted to/from a superpage. As this involves a > + * break-before-make sequence there is a short period of time where > + * an access will raise an exception. If this happens the fault > + * address will be changed to the kernel address so a later read of > + * far_el1 will give the wrong value. > + * > + * The earliest memory access that could trigger a fault is in a > + * function called by the save_registers macro so this is the latest > + * we can read the userspace value. > + */ > + mrs x19, far_el1 > save_registers 0 > ldr x0, [x18, #PC_CURTHREAD] > mov x1, sp > str x1, [x0, #TD_FRAME] > + mov x2, x19 > bl do_el0_sync > do_ast > restore_registers 0 > diff --git a/sys/arm64/arm64/trap.c b/sys/arm64/arm64/trap.c > index 4e54a06548cc..1b33d7aa60c4 100644 > --- a/sys/arm64/arm64/trap.c > +++ b/sys/arm64/arm64/trap.c > @@ -76,7 +76,7 @@ __FBSDID("$FreeBSD$"); > > /* Called from exception.S */ > void do_el1h_sync(struct thread *, struct trapframe *); This did not address my feedback regarding EL1 debug exceptions also clobbering FAR. Jess > -void do_el0_sync(struct thread *, struct trapframe *); > +void do_el0_sync(struct thread *, struct trapframe *, uint64_t far); > void do_el0_error(struct trapframe *); > void do_serror(struct trapframe *); > void unhandled_exception(struct trapframe *); > @@ -559,11 +559,11 @@ do_el1h_sync(struct thread *td, struct trapframe *frame) > } > > void > -do_el0_sync(struct thread *td, struct trapframe *frame) > +do_el0_sync(struct thread *td, struct trapframe *frame, uint64_t far) > { > pcpu_bp_harden bp_harden; > uint32_t exception; > - uint64_t esr, far; > + uint64_t esr; > int dfsc; > > /* Check we have a sane environment when entering from userland */ > @@ -573,27 +573,15 @@ do_el0_sync(struct thread *td, struct trapframe *frame) > > esr = frame->tf_esr; > exception = ESR_ELx_EXCEPTION(esr); > - switch (exception) { > - case EXCP_INSN_ABORT_L: > - far = READ_SPECIALREG(far_el1); > - > + if (exception == EXCP_INSN_ABORT_L && far > VM_MAXUSER_ADDRESS) { > /* > * Userspace may be trying to train the branch predictor to > * attack the kernel. If we are on a CPU affected by this > * call the handler to clear the branch predictor state. > */ > - if (far > VM_MAXUSER_ADDRESS) { > - bp_harden = PCPU_GET(bp_harden); > - if (bp_harden != NULL) > - bp_harden(); > - } > - break; > - case EXCP_UNKNOWN: > - case EXCP_DATA_ABORT_L: > - case EXCP_DATA_ABORT: > - case EXCP_WATCHPT_EL0: > - far = READ_SPECIALREG(far_el1); > - break; > + bp_harden = PCPU_GET(bp_harden); > + if (bp_harden != NULL) > + bp_harden(); > } > intr_enable(); >