From 921ef7b6495969fa8fb02d6b7ec9b34d80c01c6c Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Sat, 15 Jun 2019 15:04:04 -0700 Subject: [PATCH] kernel - Fix SMAP/SMEP caught user mode access part 2/2. * Finish implementing SMAP exception handling support by properly detecting it in trap() and generating a panic(). Otherwise the cpu just locks up in a page-fault loop without any indication as to why on the console. * To properly support SMAP, make sure AC is cleared on system calls (it is already cleared on any interrupt or exception by the frame push code but I missed the syscall entry code). --- sys/platform/pc64/x86_64/machdep.c | 2 +- sys/platform/pc64/x86_64/mp_machdep.c | 2 +- sys/platform/pc64/x86_64/trap.c | 61 +++++++++++++++++++++------ 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/sys/platform/pc64/x86_64/machdep.c b/sys/platform/pc64/x86_64/machdep.c index 18395d135d..a8834f1d78 100644 --- a/sys/platform/pc64/x86_64/machdep.c +++ b/sys/platform/pc64/x86_64/machdep.c @@ -2727,7 +2727,7 @@ hammer_time(u_int64_t modulep, u_int64_t physfree) msr = ((u_int64_t)GSEL(GCODE_SEL, SEL_KPL) << 32) | ((u_int64_t)GSEL(GUCODE32_SEL, SEL_UPL) << 48); wrmsr(MSR_STAR, msr); - wrmsr(MSR_SF_MASK, PSL_NT|PSL_T|PSL_I|PSL_C|PSL_D|PSL_IOPL); + wrmsr(MSR_SF_MASK, PSL_NT|PSL_T|PSL_I|PSL_C|PSL_D|PSL_IOPL|PSL_AC); getmemsize(kmdp, physfree); init_param2(physmem); diff --git a/sys/platform/pc64/x86_64/mp_machdep.c b/sys/platform/pc64/x86_64/mp_machdep.c index bff8b83894..4a312d3b4d 100644 --- a/sys/platform/pc64/x86_64/mp_machdep.c +++ b/sys/platform/pc64/x86_64/mp_machdep.c @@ -329,7 +329,7 @@ init_secondary(void) msr = ((u_int64_t)GSEL(GCODE_SEL, SEL_KPL) << 32) | ((u_int64_t)GSEL(GUCODE32_SEL, SEL_UPL) << 48); wrmsr(MSR_STAR, msr); - wrmsr(MSR_SF_MASK, PSL_NT|PSL_T|PSL_I|PSL_C|PSL_D|PSL_IOPL); + wrmsr(MSR_SF_MASK, PSL_NT|PSL_T|PSL_I|PSL_C|PSL_D|PSL_IOPL|PSL_AC); pmap_set_opt(); /* PSE/4MB pages, etc */ pmap_init_pat(); /* Page Attribute Table */ diff --git a/sys/platform/pc64/x86_64/trap.c b/sys/platform/pc64/x86_64/trap.c index a3ed6e8b35..9fd31c1053 100644 --- a/sys/platform/pc64/x86_64/trap.c +++ b/sys/platform/pc64/x86_64/trap.c @@ -356,6 +356,25 @@ userexit(struct lwp *lp) lp->lwp_proc->p_usched->acquire_curproc(lp); } +/* + * A page fault on a userspace address is classified as SMAP-induced + * if: + * - SMAP is supported + * - kernel mode accessed present data page + * - rflags.AC was cleared + */ +static int +trap_is_smap(struct trapframe *frame) +{ + if ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 && + (frame->tf_err & (PGEX_P | PGEX_U | PGEX_I | PGEX_RSV)) == PGEX_P && + (frame->tf_rflags & PSL_AC) == 0) { + return 1; + } else { + return 0; + } +} + #if !defined(KTR_KERNENTRY) #define KTR_KERNENTRY KTR_ALL #endif @@ -881,19 +900,37 @@ trap_pfault(struct trapframe *frame, int usermode) goto nogo; } - /* - * Debugging, try to catch kernel faults on the user address - * space when not inside on onfault (e.g. copyin/copyout) - * routine. - */ - if (usermode == 0 && (td->td_pcb == NULL || - td->td_pcb->pcb_onfault == NULL)) { + if (usermode == 0) { #ifdef DDB - if (freeze_on_seg_fault) { - kprintf("trap_pfault: user address fault from kernel mode " - "%016lx\n", (long)frame->tf_addr); - while (freeze_on_seg_fault) - tsleep(&freeze_on_seg_fault, 0, "frzseg", hz * 20); + /* + * Debugging, catch kernel faults on the user address + * space when not inside on onfault (e.g. copyin/ + * copyout) routine. + */ + if (td->td_pcb == NULL || + td->td_pcb->pcb_onfault == NULL) { + if (freeze_on_seg_fault) { + kprintf("trap_pfault: user address " + "fault from kernel mode " + "%016lx\n", + (long)frame->tf_addr); + while (freeze_on_seg_fault) { + tsleep(&freeze_on_seg_fault, + 0, + "frzseg", + hz * 20); + } + } + } + if (td->td_gd->gd_intr_nesting_level || + trap_is_smap(frame) || + td->td_pcb == NULL || + td->td_pcb->pcb_onfault == NULL) { + kprintf("Fatal user address access " + "from kernel mode from %s at %016jx\n", + td->td_comm, frame->tf_rip); + trap_fatal(frame, frame->tf_addr); + return (-1); } #endif } -- 2.41.0