diff options
Diffstat (limited to 'sys/arm64/arm64/trap.c')
-rw-r--r-- | sys/arm64/arm64/trap.c | 212 |
1 files changed, 134 insertions, 78 deletions
diff --git a/sys/arm64/arm64/trap.c b/sys/arm64/arm64/trap.c index 21843694f48c..6cc1933095c8 100644 --- a/sys/arm64/arm64/trap.c +++ b/sys/arm64/arm64/trap.c @@ -27,14 +27,13 @@ #include "opt_ddb.h" -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/systm.h> +#include <sys/asan.h> #include <sys/kernel.h> #include <sys/ktr.h> #include <sys/lock.h> +#include <sys/msan.h> #include <sys/mutex.h> #include <sys/proc.h> #include <sys/ptrace.h> @@ -152,7 +151,7 @@ cpu_fetch_syscall_args(struct thread *td) } if (__predict_false(sa->code >= p->p_sysent->sv_size)) - sa->callp = &p->p_sysent->sv_table[0]; + sa->callp = &nosys_sysent; else sa->callp = &p->p_sysent->sv_table[sa->code]; @@ -212,7 +211,7 @@ align_abort(struct thread *td, struct trapframe *frame, uint64_t esr, if (!lower) { print_registers(frame); print_gp_register("far", far); - printf(" esr: %.8lx\n", esr); + printf(" esr: 0x%.16lx\n", esr); panic("Misaligned access from kernel space!"); } @@ -226,27 +225,38 @@ static void external_abort(struct thread *td, struct trapframe *frame, uint64_t esr, uint64_t far, int lower) { + if (lower) { + call_trapsignal(td, SIGBUS, BUS_OBJERR, (void *)far, + ESR_ELx_EXCEPTION(frame->tf_esr)); + userret(td, frame); + return; + } /* * Try to handle synchronous external aborts caused by * bus_space_peek() and/or bus_space_poke() functions. */ - if (!lower && test_bs_fault((void *)frame->tf_elr)) { + if (test_bs_fault((void *)frame->tf_elr)) { frame->tf_elr = (uint64_t)generic_bs_fault; return; } print_registers(frame); print_gp_register("far", far); - panic("Unhandled EL%d external data abort", lower ? 0: 1); + panic("Unhandled external data abort"); } -static void +/* + * It is unsafe to access the stack canary value stored in "td" until + * kernel map translation faults are handled, see the pmap_klookup() call below. + * Thus, stack-smashing detection with per-thread canaries must be disabled in + * this function. + */ +static void NO_PERTHREAD_SSP data_abort(struct thread *td, struct trapframe *frame, uint64_t esr, uint64_t far, int lower) { struct vm_map *map; - struct proc *p; struct pcb *pcb; vm_prot_t ftype; int error, sig, ucode; @@ -268,28 +278,45 @@ data_abort(struct thread *td, struct trapframe *frame, uint64_t esr, } #endif - pcb = td->td_pcb; - p = td->td_proc; - if (lower) - map = &p->p_vmspace->vm_map; - else { - intr_enable(); - + if (lower) { + map = &td->td_proc->p_vmspace->vm_map; + } else if (!ADDR_IS_CANONICAL(far)) { /* We received a TBI/PAC/etc. fault from the kernel */ - if (!ADDR_IS_CANONICAL(far)) { - error = KERN_INVALID_ADDRESS; - goto bad_far; + error = KERN_INVALID_ADDRESS; + pcb = td->td_pcb; + goto bad_far; + } else if (ADDR_IS_KERNEL(far)) { + /* + * Handle a special case: the data abort was caused by accessing + * a thread structure while its mapping was being promoted or + * demoted, as a consequence of the break-before-make rule. It + * is not safe to enable interrupts or dereference "td" before + * this case is handled. + * + * In principle, if pmap_klookup() fails, there is no need to + * call pmap_fault() below, but avoiding that call is not worth + * the effort. + */ + if (ESR_ELx_EXCEPTION(esr) == EXCP_DATA_ABORT) { + switch (esr & ISS_DATA_DFSC_MASK) { + case ISS_DATA_DFSC_TF_L0: + case ISS_DATA_DFSC_TF_L1: + case ISS_DATA_DFSC_TF_L2: + case ISS_DATA_DFSC_TF_L3: + if (pmap_klookup(far, NULL)) + return; + break; + } } - - /* The top bit tells us which range to use */ - if (ADDR_IS_KERNEL(far)) { + intr_enable(); + map = kernel_map; + } else { + intr_enable(); + map = &td->td_proc->p_vmspace->vm_map; + if (map == NULL) map = kernel_map; - } else { - map = &p->p_vmspace->vm_map; - if (map == NULL) - map = kernel_map; - } } + pcb = td->td_pcb; /* * Try to handle translation, access flag, and permission faults. @@ -302,13 +329,20 @@ data_abort(struct thread *td, struct trapframe *frame, uint64_t esr, pmap_fault(map->pmap, esr, far) == KERN_SUCCESS) return; - KASSERT(td->td_md.md_spinlock_count == 0, - ("data abort with spinlock held")); +#ifdef INVARIANTS + if (td->td_md.md_spinlock_count != 0) { + print_registers(frame); + print_gp_register("far", far); + printf(" esr: 0x%.16lx\n", esr); + panic("data abort with spinlock held (spinlock count %d != 0)", + td->td_md.md_spinlock_count); + } +#endif if (td->td_critnest != 0 || WITNESS_CHECK(WARN_SLEEPOK | WARN_GIANTOK, NULL, "Kernel page fault") != 0) { print_registers(frame); print_gp_register("far", far); - printf(" esr: %.8lx\n", esr); + printf(" esr: 0x%.16lx\n", esr); panic("data abort in critical section or under mutex"); } @@ -334,11 +368,11 @@ data_abort(struct thread *td, struct trapframe *frame, uint64_t esr, /* Fault in the page. */ error = vm_fault_trap(map, far, ftype, VM_FAULT_NORMAL, &sig, &ucode); if (error != KERN_SUCCESS) { -bad_far: if (lower) { call_trapsignal(td, sig, ucode, (void *)far, ESR_ELx_EXCEPTION(esr)); } else { +bad_far: if (td->td_intr_nesting_level == 0 && pcb->pcb_onfault != 0) { frame->tf_x[0] = error; @@ -349,7 +383,7 @@ bad_far: printf("Fatal data abort:\n"); print_registers(frame); print_gp_register("far", far); - printf(" esr: %.8lx\n", esr); + printf(" esr: 0x%.16lx\n", esr); #ifdef KDB if (debugger_on_trap) { @@ -361,7 +395,7 @@ bad_far: return; } #endif - panic("vm_fault failed: %lx error %d", + panic("vm_fault failed: 0x%lx error %d", frame->tf_elr, error); } } @@ -380,14 +414,14 @@ print_gp_register(const char *name, uint64_t value) db_expr_t offset; #endif - printf(" %s: %16lx", name, value); + printf(" %s: 0x%.16lx", name, value); #if defined(DDB) /* If this looks like a kernel address try to find the symbol */ if (value >= VM_MIN_KERNEL_ADDRESS) { sym = db_search_symbol(value, DB_STGY_ANY, &offset); if (sym != C_DB_SYM_NULL) { db_symbol_values(sym, &sym_name, &sym_value); - printf(" (%s + %lx)", sym_name, offset); + printf(" (%s + 0x%lx)", sym_name, offset); } } #endif @@ -405,10 +439,10 @@ print_registers(struct trapframe *frame) reg); print_gp_register(name, frame->tf_x[reg]); } - printf(" sp: %16lx\n", frame->tf_sp); + printf(" sp: 0x%.16lx\n", frame->tf_sp); print_gp_register(" lr", frame->tf_lr); print_gp_register("elr", frame->tf_elr); - printf("spsr: %8x\n", frame->tf_spsr); + printf("spsr: 0x%.16lx\n", frame->tf_spsr); } #ifdef VFP @@ -434,13 +468,20 @@ fpe_trap(struct thread *td, void *addr, uint32_t exception) } #endif -void +/* + * See the comment above data_abort(). + */ +void NO_PERTHREAD_SSP do_el1h_sync(struct thread *td, struct trapframe *frame) { uint32_t exception; uint64_t esr, far; int dfsc; + kasan_mark(frame, sizeof(*frame), sizeof(*frame), 0); + kmsan_mark(frame, sizeof(*frame), KMSAN_STATE_INITED); + + far = frame->tf_far; /* Read the esr register to get the exception details */ esr = frame->tf_esr; exception = ESR_ELx_EXCEPTION(esr); @@ -450,17 +491,23 @@ do_el1h_sync(struct thread *td, struct trapframe *frame) return; #endif - CTR4(KTR_TRAP, - "do_el1_sync: curthread: %p, esr %lx, elr: %lx, frame: %p", td, - esr, frame->tf_elr, frame); + CTR4(KTR_TRAP, "%s: exception=%lu, elr=0x%lx, esr=0x%lx", + __func__, exception, frame->tf_elr, esr); /* * Enable debug exceptions if we aren't already handling one. They will * be masked again in the exception handler's epilogue. */ - if (exception != EXCP_BRK && exception != EXCP_WATCHPT_EL1 && - exception != EXCP_SOFTSTP_EL1) + switch (exception) { + case EXCP_BRK: + case EXCP_BRKPT_EL1: + case EXCP_WATCHPT_EL1: + case EXCP_SOFTSTP_EL1: + break; + default: dbg_enable(); + break; + } switch (exception) { case EXCP_FP_SIMD: @@ -472,13 +519,12 @@ do_el1h_sync(struct thread *td, struct trapframe *frame) #endif { print_registers(frame); - printf(" esr: %.8lx\n", esr); + printf(" esr: 0x%.16lx\n", esr); panic("VFP exception in the kernel"); } break; case EXCP_INSN_ABORT: case EXCP_DATA_ABORT: - far = READ_SPECIALREG(far_el1); dfsc = esr & ISS_DATA_DFSC_MASK; if (dfsc < nitems(abort_handlers) && abort_handlers[dfsc] != NULL) { @@ -486,8 +532,8 @@ do_el1h_sync(struct thread *td, struct trapframe *frame) } else { print_registers(frame); print_gp_register("far", far); - printf(" esr: %.8lx\n", esr); - panic("Unhandled EL1 %s abort: %x", + printf(" esr: 0x%.16lx\n", esr); + panic("Unhandled EL1 %s abort: 0x%x", exception == EXCP_INSN_ABORT ? "instruction" : "data", dfsc); } @@ -506,6 +552,7 @@ do_el1h_sync(struct thread *td, struct trapframe *frame) panic("No debugger in kernel."); #endif break; + case EXCP_BRKPT_EL1: case EXCP_WATCHPT_EL1: case EXCP_SOFTSTP_EL1: #ifdef KDB @@ -517,19 +564,26 @@ do_el1h_sync(struct thread *td, struct trapframe *frame) case EXCP_FPAC: /* We can see this if the authentication on PAC fails */ print_registers(frame); - printf(" far: %16lx\n", READ_SPECIALREG(far_el1)); + print_gp_register("far", far); panic("FPAC kernel exception"); break; case EXCP_UNKNOWN: if (undef_insn(1, frame)) break; - printf("Undefined instruction: %08x\n", + print_registers(frame); + print_gp_register("far", far); + panic("Undefined instruction: %08x", *(uint32_t *)frame->tf_elr); - /* FALLTHROUGH */ + break; + case EXCP_BTI: + print_registers(frame); + print_gp_register("far", far); + panic("Branch Target exception"); + break; default: print_registers(frame); - print_gp_register("far", READ_SPECIALREG(far_el1)); - panic("Unknown kernel exception %x esr_el1 %lx", exception, + print_gp_register("far", far); + panic("Unknown kernel exception 0x%x esr_el1 0x%lx", exception, esr); } } @@ -544,38 +598,29 @@ do_el0_sync(struct thread *td, struct trapframe *frame) /* Check we have a sane environment when entering from userland */ KASSERT((uintptr_t)get_pcpu() >= VM_MIN_KERNEL_ADDRESS, - ("Invalid pcpu address from userland: %p (tpidr %lx)", + ("Invalid pcpu address from userland: %p (tpidr 0x%lx)", get_pcpu(), READ_SPECIALREG(tpidr_el1))); + kasan_mark(frame, sizeof(*frame), sizeof(*frame), 0); + kmsan_mark(frame, sizeof(*frame), KMSAN_STATE_INITED); + + far = frame->tf_far; 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(); - CTR4(KTR_TRAP, - "do_el0_sync: curthread: %p, esr %lx, elr: %lx, frame: %p", td, esr, - frame->tf_elr, frame); + CTR4(KTR_TRAP, "%s: exception=%lu, elr=0x%lx, esr=0x%lx", + __func__, exception, frame->tf_elr, esr); switch (exception) { case EXCP_FP_SIMD: @@ -612,8 +657,8 @@ do_el0_sync(struct thread *td, struct trapframe *frame) else { print_registers(frame); print_gp_register("far", far); - printf(" esr: %.8lx\n", esr); - panic("Unhandled EL0 %s abort: %x", + printf(" esr: 0x%.16lx\n", esr); + panic("Unhandled EL0 %s abort: 0x%x", exception == EXCP_INSN_ABORT_L ? "instruction" : "data", dfsc); } @@ -677,6 +722,11 @@ do_el0_sync(struct thread *td, struct trapframe *frame) (void *)frame->tf_elr, exception); userret(td, frame); break; + case EXCP_BTI: + call_trapsignal(td, SIGILL, ILL_ILLOPC, (void *)frame->tf_elr, + exception); + userret(td, frame); + break; default: call_trapsignal(td, SIGBUS, BUS_OBJERR, (void *)frame->tf_elr, exception); @@ -699,12 +749,15 @@ do_serror(struct trapframe *frame) { uint64_t esr, far; - far = READ_SPECIALREG(far_el1); + kasan_mark(frame, sizeof(*frame), sizeof(*frame), 0); + kmsan_mark(frame, sizeof(*frame), KMSAN_STATE_INITED); + + far = frame->tf_far; esr = frame->tf_esr; print_registers(frame); print_gp_register("far", far); - printf(" esr: %.8lx\n", esr); + printf(" esr: 0x%.16lx\n", esr); panic("Unhandled System Error"); } @@ -713,11 +766,14 @@ unhandled_exception(struct trapframe *frame) { uint64_t esr, far; - far = READ_SPECIALREG(far_el1); + kasan_mark(frame, sizeof(*frame), sizeof(*frame), 0); + kmsan_mark(frame, sizeof(*frame), KMSAN_STATE_INITED); + + far = frame->tf_far; esr = frame->tf_esr; print_registers(frame); print_gp_register("far", far); - printf(" esr: %.8lx\n", esr); + printf(" esr: 0x%.16lx\n", esr); panic("Unhandled exception"); } |